netlib.narod.ru | < Назад | Оглавление | Далее > |
Для начала давайте создадим программу, печатающую те строки вводимого текста, в которых содержится некоторый «образец», заданный в виде строки символов. (Эта программа представляет собой частный случай программы 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, если совпадений со строкой 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 && c != '\n') s[i++] = c; if (c == '\n') s[i++] = c; s[i] = '\0'; return i; } /* strindex: ищет строку t в строке s; возвращает место строки t или -1, если образец не найден */ 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 есть выражения, а у других — нет. Во всех случаях, когда функция «забыла» передать результат, в вызывающую функцию возвращается «мусор».
Функция main в программе поиска по образцу возвращает в качестве результата количество найденных строк. Это число доступно той среде, из которой данная программа была вызвана.
Механизмы компиляции и загрузки программ на Си, расположенных в нескольких исходных файлах, зависят от системы. В системе UNIX, например, эти работы выполняет упомянутая в главе 1 команда cc. Предположим, что три функции нашего последнего примера расположены в трех разных файлах: main.c, getline.c и strindex.c. Тогда команда
cc main.c getline.c strindex.c
скомпилирует указанные файлы, поместив результат компиляции в файлы объектных модулей main.o, getline.o и strindex.o, и затем загрузит их в исполняемый файл a.out. Если обнаружилась ошибка, например в файле main.c, то его можно скомпилировать снова и результат скомпоновать с ранее полученными объектными файлами, выполнив следующую команду:
cc main.c getline.o strindex.o
Команда cc использует стандартные расширения файлов «.c» и «.o», чтобы отличать исходные файлы от объектных.
Упражнение 4-1 |
Напишите функцию strindex(s, t), которая выдает позицию самого правого вхождения строки t в строку s или –1, если вхождения не обнаружено. |
netlib.narod.ru | < Назад | Оглавление | Далее > |