4.1. Основные сведения
Для начала давайте разработаем и составим программу печати каждой строки ввода, которая содержит определенную комбинацию символов. /Это -- специальный случай утилиты GREP системы "UNIX"/. Например, при поиске комбинации "THE" в наборе строк
NOW IS THE TIME FOR ALL GOOD MEN TO COME TO THE AID OF THEIR PARTY
в качестве выхода получим
NOW IS THE TIME MEN TO COME TO THE AID OF THEIR PARTY
основная схема выполнения задания четко разделяется на три части:
WHILE (имеется еще строка) IF (строка содержит нужную комбинацию) вывод этой строки
Конечно, возможно запрограммировать все действия в виде одной основной процедуры, но лучше использовать естественную структуру задачи и представить каждую часть в виде отдельной функции. С тремя маленькими кусками легче иметь дело, чем с одним большим, потому что отдельные не относящиеся к существу дела детали можно включить в функции и уменьшить возможность нежелательных взаимодействий. Кроме того, эти куски могут оказаться полезными сами по себе.
"Пока имеется еще строка" -- это GETLINE, функция, которую мы запрограммировали в главе 1, а "вывод этой строки" -- это функция PRINTF, которую уже кто-то подготовил для нас. Это значит, что нам осталось только написать процедуру для определения, содержит ли строка данную комбинацию символов или нет. Мы можем решить эту проблему, позаимствовав разработку из PL/1: функция INDEX(S,т) возвращает позицию, или индекс, строки S, где начинается строка T, и -1, если S не содержит т . В качестве начальной позиции мы используем 0, а не 1, потому что в языке "C" массивы начинаются с позиции нуль. Когда нам в дальнейшем понадобится проверять на совпадение более сложные конструкции, нам придется заменить только функцию INDEX; остальная часть программы останется той же самой.
После того, как мы потратили столько усилий на разработку, написание программы в деталях не представляет затруднений. ниже приводится целиком вся программа, так что вы можете видеть, как соединяются вместе отдельные части. Комбинация символов, по которой производится поиск, выступает пока в качестве символьной строки в аргументе функции INDEX, что не является самым общим механизмом. Мы скоро вернемся к обсуждению вопроса об инициализации символьных массивов и в главе 5 покажем, как сделать комбинацию символов параметром, которому присваивается значение в ходе выполнения программы. Программа также содержит новый вариант функции GETLINE; вам может оказаться полезным сравнить его с вариантом из главы 1.
#DEFINE MAXLINE 1000 MAIN() /* FIND ALL LINES MATCHING A PATTERN */ { CHAR LINE[MAXLINE]; WHILE (GETLINE(LINE, MAXLINE) > 0) IF (INDEX(LINE, "THE") >= 0) PRINTF("%S", LINE); } GETLINE(S, LIM) /* GET LINE INTO S, RETURN LENGTH */ CHAR S[]; INT LIM; { INT C, I; I = 0; WHILE(--LIM>0 && (C=GETCHAR()) != EOF && C != '\N') S[I++] = C; IF (C == '\N') S[I++] = C; S[I] = '\0'; RETURN(I); } INDEX(S,T) /* RETURN INDEX OF T IN S,-1 IF NONE */ CHAR S[], 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 (T[K] == '\0') RETURN(I); } RETURN(-1); }
Каждая функция имеет вид имя (список аргументов, если они имеются) описания аргументов, если они имеются
{ описания и операторы , если они имеются }
Как и указывается, некоторые части могут отсутствовать; минимальной функцией является
DUMMY () { }
которая не совершает никаких действий.
/Такая ничего не делающая функция иногда оказывается удобной для сохранения места для дальнейшего развития программы/. если функция возвращает что-либо отличное от целого значения, то перед ее именем может стоять указатель типа; этот вопрос обсуждается в следующем разделе.
Программой является просто набор определений отдельных функций. Связь между функциями осуществляется через аргументы и возвращаемые функциями значения /в этом случае/; ее можно также осуществлять через внешние переменные. Функции могут располагаться в исходном файле в любом порядке, а сама исходная программа может размещаться на нескольких файлах, но так, чтобы ни одна функция не расщеплялась.
Оператор RETURN служит механизмом для возвращения значения из вызванной функции в функцию, которая к ней обратилась. За RETURN может следовать любое выражение:
RETURN (выражение)
Вызывающая функция может игнорировать возвращаемое значение, если она этого пожелает. Более того, после RETURN может не быть вообще никакого выражения; в этом случае в вызывающую программу не передается никакого значения. Управление также возвращется в вызывающую программу без передачи какого-либо значения и в том случае, когда при выполнении мы "проваливаемся" на конец функции, достигая закрывающейся правой фигурной скобки. Eсли функция возвращает значение из одного места и не возвращает никакого значения из другого места, это не является незаконным, но может быть признаком каких-то неприятностей. В любом случае "значением" функции, которая не возвращает значения, несомненно будет мусор. Отладочная программа LINT проверяет такие ошибки.
Механика компиляции и загрузки "C"-программ, расположенных в нескольких исходных файлах, меняется от системы к системе. В системе "UNIX", например, эту работу выполняет команда 'CC', упомянутая в главе 1. Предположим, что три функции находятся в трех различных файлах с именами MAIN.C, GETLINE.C и INDEX.C . Тогда команда
CC MAIN.C GETLINE.C INDEX.C
компилирует эти три файла, помещает полученный настраиваемый объектный код в файлы MAIN.O, GETLINE.O и INDEX.O и загружает их всех в выполняемый файл, называемый A.OUT.
Если имеется какая-то ошибка, скажем в MAIN.C, то этот файл можно перекомпилировать отдельно и загрузить вместе с предыдущими объектными файлами по команде
CC MAIN.C GETLIN.O INDEX.O
Команда 'CC' использует соглашение о наименовании с ".с" и ".о" для того, чтобы отличить исходные файлы от объектных.
Упражнение 4-1.
----------------
Составьте программу для функции RINDEX(S,T), которая возвращает позицию самого правого вхождения т в S и -1, если S не содержит T.
ПРЕДЫДУЩАЯ ЧАСТЬ 4. Функции и структура программ |
СОДЕРЖАНИЕ | СЛЕДУЮЩАЯ ЧАСТЬ 4.2. Функции, возвращающие нецелые значения |