Основные сведения
Для начала давайте разработаем и составим программу пе- чати каждой строки ввода, которая содержит определенную ком- бинацию символов. /Это - специальный случай утилиты 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.с, GETLINE.C и INDEX.с . Тогда команда
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.