|
Функции и структура программ
4. Функции и структура программ
Функции разбивают большие вычислительные задачи на маленькие подзадачи и позволяют использовать в работе то, что уже сделано другими, а не начинать каждый раз с пустого места. Соответствующие функции часто могут скрывать в себе детали проводимых в разных частях программы операций, знать которые нет необходимости, проясняя тем самым всю программу, как целое, и облегчая мучения при внесении изменений.
Язык "C" разрабатывался со стремлением сделать функции эффективными и удобными для использования; "C"- программы обычно состоят из большого числа маленьких функций, а не из нескольких больших. Программа может размещаться в одном или нескольких исходных файлах любым удобным образом; исходные файлы могут компилироваться отдельно и загружаться вместе наряду со скомпилированными ранее функциями из библиотек. Мы здесь не будем вдаваться в детали этого процесса, поскольку они зависят от используемой системы.
Большинство программистов хорошо знакомы с "библиотечными" функциями для ввода и вывода / getchar, putchar / и для численных расчетов / sin, cos, sqrt /. В этой лекции мы сообщим больше о написании новых функций.
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,t) возвращает позицию, или индекс, строки s, где начинается строка t, и -1, если s не содержит t. В качестве начальной позиции мы используем 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 getline.o index.o
Команда ' cc ' использует соглашение о наименовании с ".с" и ".о" для того, чтобы отличить исходные файлы от объектных.
Упражнение 4-1
Составьте программу для функции rindex(s,t), которая возвращает позицию самого правого вхождения t в s и -1, если s не содержит t.