- •Глава 8. Интерфейс с системой unix
- •Приложение a. Справочное руководство
- •Приложение b. Стандартная библиотека
- •Приложение с. Перечень изменений
- •Предметный указатель
- •Предисловие
- •Предисловие к первому изданию
- •Введение
- •Глава 1. Обзор языка
- •1.1 Начнем, пожалуй
- •1.2 Переменные и арифметические выражения
- •1.3 Инструкция for
- •1.4 Именованные константы
- •1.5 Ввод-вывод символов
- •1.5.1 Копирование файла
- •1.5.2 Подсчет символов
- •1.5.3 Подсчет строк
- •1.5.4 Подсчет слов
- •1.6 Массивы
- •1.7 Функции
- •1.8 Аргументы. Вызов по значению
- •1.9 Символьные массивы
- •1.10 Внешние переменные и область видимости
- •Глава 2. Типы, операторы и выражения
- •2.1 Имена переменных
- •2.2 Типы и размеры данных
- •2.3 Константы
- •2.4 Объявления
- •2.5 Арифметические операторы
- •2.6 Операторы отношения и логические операторы
- •2.7 Преобразования типов
- •2.8 Операторы инкремента и декремента
- •2.9 Побитовые операторы
- •2.10 Операторы и выражения присваивания
- •2.11 Условные выражения
- •2.12 Приоритет и очередность вычислений
- •Глава 3. Управление
- •3.1 Инструкции и блоки
- •3.2 Конструкция if-else
- •3.3 Конструкция else-if
- •3.4 Переключатель switch
- •3.5 Циклы while и for
- •3.6 Цикл do-while
- •3.7 Инструкции break и continue
- •3.8 Инструкция goto и метки
- •Глава 4. Функции и структура программы
- •4.1 Основные сведения о функциях
- •4.2 Функции, возвращающие нецелые значения
- •4.3 Внешние переменные
- •4.4 Области видимости
- •4.5 Заголовочные файлы
- •4.6 Статические переменные
- •4.7 Регистровые переменные
- •4.8 Блочная структура
- •4.9 Инициализация
- •4.10 Рекурсия
- •4.11 Препроцессор языка Си
- •4.11.1 Включение файла
- •4.11.2 Макроподстановка
- •4.11.3 Условная компиляция
- •Глава 5. Указатели и массивы
- •5.1 Указатели и адреса
- •5.2 Указатели и аргументы функций
- •5.3 Указатели и массивы
- •5.4 Адресная арифметика
- •5.5 Символьные указатели функции
- •5.6 Массивы указателей, указатели на указатели
- •5.7 Многомерные массивы
- •5.8 Инициализация массивов указателей
- •5.9 Указатели против многомерных массивов
- •5.10 Аргументы командной строки
- •5.11 Указатели на функции
- •5.12 Сложные объявления
- •Глава 6. Структуры
- •6.2 Структуры и функции
- •6.3 Массивы структур
- •6.4 Указатели на структуры
- •6.5 Структуры со ссылками на себя
- •6.6 Просмотр таблиц
- •6.7 Средство typedef
- •6.8 Объединения
- •6.9 Битовые поля
- •Глава 7. Ввод и вывод
- •7.1 Стандартный ввод-вывод
- •7.2 Форматный вывод (printf)
- •7.3 Списки аргументов переменной длины
- •7.4 Форматный ввод (scanf)
- •7.5 Доступ к файлам
- •7.6 Управление ошибками (stderr и exit)
- •7.7 Ввод-вывод строк
- •7.8 Другие библиотечные функции
- •7.8.1 Операции со строками
- •7.8.2 Анализ класса символов и преобразование символов
- •7.8.3 Функция ungetc
- •7.8.4 Исполнение команд операционной системы
- •7.8.5 Управление памятью
- •7.8.6 Математические функции
- •7.8.7 Генератор случайных чисел
- •Глава 8. Интерфейс с системой unix
- •8.1 Дескрипторы файлов
- •8.2 Нижний уровень ввода-вывода (read и write)
- •8.3 Системные вызовы open, creat, close, unlink
- •8.4 Произвольный доступ (lseek)
- •8.5 Пример. Реализация функций fopen и getc
- •8.6 Пример. Печать каталогов
- •8.7 Пример. Распределитель памяти
- •Приложение a. Справочное руководство
- •A1. Введение
- •A2. Соглашения о лексике
- •A2.1. Лексемы (tokens)
- •A2.2. Комментарий
- •A2.3. Идентификаторы
- •A2.4. Ключевые слова
- •Volatile
- •A2.5. Константы
- •A2.5.1. Целые константы
- •A2.5.2. Символьные константы
- •А2.5.3. Константы с плавающей точкой
- •A2.5.4. Константы-перечисления
- •A2.6. Строковые литералы
- •A3. Нотация синтаксиса
- •A4. Что обозначают идентификаторы
- •A4.1. Класс памяти
- •A4.2. Базовые типы
- •A4.3. Производные типы
- •A4.4. Квалификаторы типов
- •A5. Объекты и Lvalues
- •A6. Преобразования
- •A6.1. Целочисленное повышение
- •A6.2. Целочисленные преобразования
- •A6.3. Целые и числа с плавающей точкой
- •A6.4. Типы с плавающей точкой
- •А6.5. Арифметические преобразования
- •A6.6. Указатели и целые
- •A6.7. Тип void
- •А6.8. Указатели на void
- •A7. Выражения
- •A7.1. Генерация указателя
- •A7.2. Первичные выражения
- •A7.3. Постфиксные выражения
- •A7.3.1. Обращение к элементам массива
- •A7.3.2. Вызов функции
- •A7.3.3. Обращение к структурам
- •A7.3.4. Постфиксные операторы инкремента и декремента
- •А7.4. Унарные операторы
- •А7.4.1. Префиксные операторы инкремента и декремента
- •A7.4.6. Оператор побитового отрицания
- •A7.4.7. Оператор логического отрицания
- •A7.4.8. Оператор определения размера sizeof
- •A7.5. Оператор приведения типа
- •A7.6. Мультипликативные операторы
- •A7.7. Аддитивные операторы
- •A7.8. Операторы сдвига
- •A7.9. Операторы отношения
- •A7.10. Операторы равенства
- •A7.15. Оператор логического или
- •А7.16. Условный оператор
- •A7.17. Выражения присваивания
- •A7.18. Оператор запятая
- •A7.19. Константные выражения
- •A8. Объявления
- •A8.1. Спецификаторы класса памяти
- •А8.2. Спецификаторы типа
- •A8.3. Объявления структур и объединений
- •A8.4. Перечисления
- •А8.5. Объявители
- •A8.6. Что означают объявители
- •A8.6.1. Объявители указателей
- •А8.6.2. Объявители массивов
- •А8.6.3. Объявители функций
- •A8.7. Инициализация
- •A8.8. Имена типов
- •А8.9. Объявление typedef
- •A8.10. Эквивалентность типов
- •A9. Инструкции
- •A9.1. Помеченные инструкции
- •A9.2. Инструкция-выражение
- •A9.3. Составная инструкция
- •A9.4. Инструкции выбора
- •A9.5. Циклические инструкции
- •A9.6. Инструкции перехода
- •А10. Внешние объявления
- •A10.1. Определение функции
- •A10.2. Внешние объявления
- •A11. Область видимости и связи
- •A11.1. Лексическая область видимости
- •A11.2. Связи
- •A12. Препроцессирование
- •A12.1. Трехзнаковые последовательности
- •A12.2. Склеивание строк
- •А12.3. Макроопределение и макрорасширение
- •A12.4. Включение файла
- •A12.5. Условная компиляция
- •A12.6. Нумерация строк
- •A13. Грамматика
- •Приложение b. Стандартная библиотека
- •B1.1. Операции над файлами
- •B1.2. Форматный вывод
- •B1.3. Форматный ввод
- •B1.4. Функции ввода-вывода символов
- •B1.5. Функции прямого ввода-вывода
- •B1.6. Функции позиционирования файла
- •B1.7. Функции обработки ошибок
- •Приложение c. Перечень изменений
4.1 Основные сведения о функциях
Начнем с того, что сконструируем программу, печатающую те строки вводимого текста, в которых содержится некоторый "образец", заданный в виде строки символов. (Эта программа представляет собой частный случай функции grep системы UNIX.) Рассмотрим пример: в результате поиска образца "ould" в строках текста
Ah Love! could you and I with Fate conspire
To grasp this sorry Scheme of Things entire,
Would not we shatter it to bits -- and then
Re-mould it nearer to The Heart's Desire!
мы получим
Ah Love! could you and I with Fate conspire
Would not we shatter it to bits — and then
Re-mould it nearer to the Heart's Desire!
Работа по поиску образца четко распадается на три этапа:
while (существует еще строка)
if (строка содержит образец)
напечатать ее
Хотя все три составляющие процесса поиска можно поместить в функцию main, все же лучше сохранить приведенную структуру и каждую ее часть реализовать в виде отдельной функции. Легче иметь дело с тремя небольшими частями, чем с одной большой, поскольку, если несущественные особенности реализации скрыты в функциях, вероятность их нежелательного воздействия друг на друга минимальна. Кроме того, оформленные в виде функций соответствующие части могут оказаться полезными и в других программах.
Конструкция "while (существует еще строка)" реализована в getline (см. главу 1), а фразу "напечатать ее" можно записать с помощью готовой функции printf. Таким образом, нам остается перевести на Си только то, что определяет, входит ли заданный образец в строку.
Чтобы решить эту задачу, мы напишем функцию strindex(s,t), которая указывает место (индекс) в строке s, где начинается строка t, или -1, если s не содержит t. Так как в Си нумерация элементов в массивах начинается с нуля, отрицательное число -1 подходит в качестве признака неудачного поиска. Если далее нам потребуется более сложное отождествление по образцу, мы просто заменим strindex на другую функцию, оставив при этом остальную часть программы без изменений. (Библиотечная функция strstr аналогична функции strindex и отличается от последней только тем, что возвращает не индекс, а указатель.)
После такого проектирования программы ее "деталировка" оказывается очевидной. Мы имеем представление о программе в целом и знаем, как взаимодействуют ее части. В нашей программе образец для поиска задается строкой-литералом, что снижает ее универсальность. В главе 5 мы еще вернемся к проблеме инициализации символьных массивов и покажем, как образец сделать параметром, устанавливаемым при запуске программы. Здесь приведена несколько измененная версия функции getline, и было бы поучительно сравнить ее с версией, рассмотренной в главе 1.
#include <stdio.h>
#define MAXLINE 1000 /* максимальный размер вводимой строки */
int getline(char line[], int max);
int strindex(char source[], char searchfor[]);
char pattern[] ="ould"; /* образец для поиска */
/* найти все строки, содержащие образец */
main()
{
char line[MAXLINE];
int found = 0;
while (getline(line, MAXLINE) > 0)
if (strindex(line, pattern) >= 0) {
printf ("%s", line);
found++;
}
return found;
}
/* getline: читает строку в s, возвращает длину */
int getline(char s[], int lim)
{
int c, i;
i = 0;
while (--lim > 0 && (c=getchar()) != EOF && с != '\n') /* I.B.: misprint was here -lim instead of --lim */
s[i++] = c;
if (c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
/* strindex: вычисляет место t в s или выдает -1, если t нет в s */
int strindex (char s[], char t[])
{
int i, j, k;
for (i = 0; s[i] != '\0'; i++) {
for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++)
;
if (k > 0 && t[k] == '\0')
return i;
}
return –1;
}
Определение любой функции имеет следующий вид:
тип-результата имя-функции (объявления аргументов)
{
объявления и инструкции
}
Отдельные части определения могут отсутствовать, как, например, в определении "минимальной" функции
dummy() { }
которая ничего не вычисляет и ничего не возвращает. Такая ничего не делающая функция в процессе разработки программы бывает полезна в качестве "хранителя места". Если тип результата опущен, то предполагается, что функция возвращает значение типа int.
Любая программа - это просто совокупность определений переменных и функций. Связи между функциями осуществляются через аргументы, возвращаемые значения и внешние переменные. В исходном файле функции могут располагаться в любом порядке; исходную программу можно разбивать на любое число файлов, но так, чтобы ни одна из функций не оказалась разрезанной.
Инструкция return реализует механизм возврата результата от вызываемой функции к вызывающей. За словом return может следовать любое выражение:
return выражение;
Если потребуется, выражение будет приведено к возвращаемому типу функции. Часто выражение заключают в скобки, но они не обязательны.
Вызывающая функция вправе проигнорировать возвращаемое значение. Более того, выражение в return может отсутствовать, и тогда вообще никакое значение не будет возвращено в вызывающую функцию. Управление возвращается в вызывающую функцию без результирующего значения также и в том случае, когда вычисления достигли "конца" (т. е. последней закрывающей фигурной скобки функции). Не запрещена (но должна вызывать настороженность) ситуация, когда в одной и той же функции одни return имеют при себе выражения, а другие - не имеют. Во всех случаях, когда функция "забыла" передать результат в return, она обязательно выдаст "мусор".
Функция main в программе поиска по образцу возвращает в качестве результата количество найденных строк. Это число доступно той среде, из которой данная программа была вызвана.
Механизмы компиляции и загрузки Си-программ, расположенных в нескольких исходных файлах, в разных системах могут различаться. В системе UNIX, например, эти работы выполняет упомянутая в главе 1 команда cc. Предположим, что три функции нашего последнего примера расположены в трех разных файлах: main.с, getline.c и strindex.c. Тогда команда
cc main.с getline.c strindex.c
скомпилирует указанные файлы, поместив результат компиляции в файлы объектных модулей main.o, getline.o и strindex.o, и затем загрузит их в исполняемый файл a.out. Если обнаружилась ошибка, например в файле main.с, то его можно скомпилировать снова и результат загрузить ранее полученными объектными файлами, выполнив следующую команду:
cc main.с getline.o strindex.o
Команда cc использует стандартные расширения файлов ".с" и ".о", чтобы отличать исходные файлы от объектных.
Упражнение 4.1. Напишите функцию strindex(s, t), которая выдает позицию самого правого вхождения t в s или -1, если вхождения не обнаружено.