Реферат: Язык С
чати каждой строки ввода, которая
содержит определенную ком-
бинацию символов. /Это - специальный
случай утилиты 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 (строка содержит нужную
комбинацию)
вывод этой строки
·
74 -
Конечно, возможно
запрограммировать все действия в виде
одной основной процедуры, но лучше
использовать естественную
структуру задачи и представить
каждую часть в виде отдельной
функции. С тремя маленькими кусками
легче иметь дело, чем с
одним большим, потому что отдельные
не относящиеся к сущест-
ву дела детали можно включить в
функции и уменьшить возмож-
ность нежелательных взаимодействий.
Кроме того, эти куски
могут оказаться полезными сами по
себе.
“Пока имеется еще строка” -
это 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);
}
·
75 -
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 () { }
которая не совершает никаких
действий.
/Такая ничего не делающая
функция иногда оказывается
удобной для сохранения места для
дальнейшего развития прог-
раммы/. если функция возвращает
что-либо отличное от целого
значения, то перед ее именем может
стоять указатель типа;
этот вопрос обсуждается в следующем
разделе.
·
76 -
Программой является просто
набор определений отдельных
функций. Связь между функциями
осуществляется через аргумен-
ты и возвращаемые функциями значения
/в этом случае/; ее
можно также осуществлять через
внешние переменные. Функции
могут располагаться в исходном
файле в любом порядке, а сама
исходная программа может
размещаться на нескольких файлах,
но так, чтобы ни одна функция не
расщеплялась.
Оператор 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.
·
77 -
4.2. Функции,
возвращающие нецелые значения.
До сих пор ни одна из
наших программ не содержала како-
го-либо описания типа функции. Дело
в том, что по умолчанию
функция неявно описывается своим
появлением в выражении или
операторе, как, например, в
WHILE (GETLINE(LINE, MAXLINE)
> 0)
Если некоторое имя, которое не
было описано ранее, появ-
ляется в выражении и за ним следует
левая круглая скобка, то
оно по контексту считается именем
некоторой функции. Кроме
того, по умолчанию предполагается,
что эта функция возвраща-
ет значение типа INT. Так как в
выражениях CHAR преобразует-
ся в INT, то нет необходимости
описывать функции, возвращаю-
щие CHAR. Эти предположения
покрывают большинство случаев,
включая все приведенные до сих пор
примеры.
Но что происходит, если
функция должна возвратить значе-
ние какого-то другого типа ? Многие
численные функции, такие
как SQRT, SIN и COS возвращают
DOUBLE; другие специальные
функции возвращают значения других
типов. Чтобы показать,
как поступать в этом случае,
давайте напишем и используем
функцию ATоF(S), которая
преобразует строку S в эквивалент-
ное ей плавающее число двойной
точности. Функция ATоF явля-
ется расширением атоI, варианты
которой мы написали в главах
2 и 3; она обрабатывает
необязательно знак и десятичную точ-
ку, а также целую и дробную часть,
каждая из которых может
как присутствовать, так и
отсутствовать./эта процедура пре-
образования ввода не очень высокого
качества; иначе она бы
заняла больше места, чем нам
хотелось бы/.
Во-первых, сама ATоF должна
описывать тип возвращаемого
ею значения, поскольку он отличен
от INT. Так как в выраже-
ниях тип FLOAT преобразуется в
DOUBLE, то нет никакого смыс-
ла в том, чтобы ATOF возвращала
FLOAT; мы можем с равным ус-
пехом воспользоваться
дополнительной точностью, так что мы
полагаем, что возвращаемое значение
типа DOUBLE. Имя типа
должно стоять перед именем функции,
как показывается ниже:
DOUBLE ATOF(S) /* CONVERT
STRING S TO DOUBLE */
CHAR S[];
{
DOUBLE VAL, POWER;
INT I, SIGN;
·
78 -
FOR(I=0; S[I]==' ' \!\!
S[I]=='\N' \!\! S[I]=='\T'; I++)
; /*
SKIP WHITE SPACE */
SIGN = 1;
IF (S[I] ==
'+' \!\! S[I] == '-') /* SIGN */
SIGN = (S[I++] == '+') ? 1 : -1;
FOR (VAL = 0; S[I] >= '0' &&
S[I] <= '9'; I++)
VAL = 10 * VAL + S[I] - '0';
IF (S[I] == '.')
I++;
FOR (POWER = 1; S[I] >= '0'
&& S[I] <= '9'; I++) {
VAL = 10 * VAL + S[I] - '0';
POWER *= 10;
}
RETURN(SIGN * VAL / POWER);
}
Вторым, но столь же важным,
является то, что вызывающая
функция должна объявить о том, что
ATOF возвращает значение,
отличное от INT типа. Такое
объявление демонстрируется на
примере следующего примитивного
настольного калькулятора
/едва пригодного для подведения
баланса в чековой книжке/,
который считывает по одному числу
на строку, причем это чис-
ло может иметь знак, и складывает
все числа, печатая сумму
после каждого ввода.
#DEFINE MAXLINE 100
MAIN() /* RUDIMENTARY DESK
CALKULATOR */
{
DOUBLE SUM, ATOF();
CHAR LINE[MAXLINE];
SUM = 0;
WHILE (GETLINE(LINE, MAXLINE) > 0)
PRINTF(“\T%.2F\N”,SUM+=ATOF(LINE));
Оисание
DOUBLE SUM, ATOF();
говорит, что SUM является переменной
типа DOUBLE , и что
ATOF является функцией,
возвращающей значение типа DOUBLE .
Эта мнемоника означает, что
значениями как SUM, так и
ATOF(...) являются плавающие числа
двойной точности.
·
79 -
Если функция ATOF не будет
описана явно в обоих местах,
то в “C” предполагается, что она
возвращает целое значение,
и вы получите бессмысленный ответ.
Если сама ATOF и обраще-
ние к ней в MAIN имеют
несовместимые типы и находятся в од-
ном и том же файле, то это будет
обнаружено компилятором. Но
если ATOF была скомпилирована
отдельно /что более вероятно/,
то это несоответствие не будет
зафиксировано, так что ATOF
будет возвращать значения типа
DOUBLE, с которым MAIN будет
обращаться, как с INT , что
приведет к бессмысленным резуль-
татам. /Программа LINT вылавливает
эту ошибку/.
Имея ATOF, мы, в принципе,
могли бы с ее помощью напи-
сать ATOI (преобразование
строки в INT):
ATOI(S) /* CONVERT STRING S TO
INTEGER */
CHAR S[];
{
DOUBLE ATOF();
RETURN(ATOF(S));
}
Обратите внимание на структуру
описаний и оператор RETURN.
Значение выражения в
RETURN (выражение)
всегда преобразуется к типу функции
перед выполнением самого
возвращения. Поэтому при появлении
в операторе RETURN значе-
ние функции атоF, имеющее тип
DOUBLE, автоматически преобра-
зуется в INT, поскольку функция
ATOI возвращает INT. (Как
обсуждалось в главе 2,
преобразование значения с плавающей
точкой к типу INT осуществляется
посредством отбрасывания
дробной части).
Упражнение 4-2.
Расширьте ATOF таким образом,
чтобы она могла работать с
числами вида
123.45е-6
где за числом с плавающей точкой
может следовать 'E' и пока-
затель экспоненты, возможно со
знаком.
4.3. Еще об аргументах функций.
В главе 1 мы уже обсуждали
тот факт , что аргументы фун-
кций передаются по значению, т.е.
вызванная функция получает
свою временную копию каждого
аргумента, а не его адрес. это
означает, что вызванная функция не
может воздействовать на
исходный аргумент в вызывающей
функции. Внутри функции каж-
дый аргумент по существу является
локальной переменной, ко-
торая инициализируется тем
значением, с которым к этой функ-
ции обратились.
·
80 -
Если в качестве аргумента
функции выступает имя массива,
то передается адрес начала этого
массива; сами элементы не
копируются. Функция может изменять
элементы массива, исполь-
зуя индексацию и адрес начала.
Таким образом, массив переда-
ется по ссылке. В главе 5 мы
обсудим, как использование ука-
зателей позволяет функциям
воздействовать на отличные от
массивов переменные в вызывающих
функциях.
Между прочим, несуществует
полностью удовлетворительного
способа написания переносимой
функции с переменным числом
аргументов. Дело в том, что нет
переносимого способа, с по-
мощью которого вызванная функция
могла бы определить, сколь-
ко аргументов было фактически
передано ей в данном обраще-
нии. Таким образом, вы, например,
не можете написать дейст-
вительно переносимую функцию,
которая будет вычислять макси-
мум от произвольного числа
аргументов, как делают встроенные
функции MAX в фортране и PL/1.
Обычно со случаем переменного
числа аргументов безопасно
иметь дело, если вызванная функция
не использует аргументов,
которые ей на самом деле не были
переданы, и если типы сог-
ласуются. Самая распространенная в
языке “C” функция с пере-
менным числом - PRINTF . Она
получает из первого аргумента
информацию, позволяющую определить
количество остальных ар-
гументов и их типы. Функция PRINTF
работает совершенно неп-
равильно, если вызывающая функция
передает ей недостаточное
количество аргументов, или если их
типы не согласуются с ти-
пами, указанными в первом
аргументе. Эта функция не является
переносимой и должна
модифицироваться при использовании в
различных условиях.
Если же типы аргументов
известны, то конец списка аргу-
ментов можно отметить, используя
какое-то соглашение; напри-
мер, считая, что некоторое
специальное значение аргумента
(часто нуль) является признаком
конца аргументов.
4.4. Внешние переменные.
Программа на языке “C”
состоит из набора внешних объек-
тов, которые являются либо
переменными, либо функциями. Тер-
мин “внешний” используется главным
образом в противопостав-
ление термину “внутренний”, которым
описываются аргументы и
автоматические переменные,
определенные внурти функций.
Внешние переменные определены вне
какой-либо функции и, та-
ким образом, потенциально доступны
для многих функций. Сами
функции всегда являются внешними,
потому что правила языка
“C” не разрешают определять одни
функции внутри других. По
умолчанию внешние переменные
являются также и “глобальными”,
так что все ссылки на такую
переменную, использующие одно и
то же имя (даже из функций,
скомпилированных независимо),
будут ссылками на одно и то же. В
этом смысле внешние пере-
менные аналогичны переменным COмMON
в фортране и EXTERNAL в
PL/1. Позднее мы покажем, как определить
внешние переменные
и функции таким образом, чтобы они
были доступны не глобаль-
но, а только в пределах одного
исходного файла.
·
81 -
В силу своей глобальной
доступности внешние переменные
предоставляют другую, отличную от
аргументов и возвращаемых
значений, возможность для обмена
данными между функциями.
Если имя внешней переменной
каким-либо образом описано, то
любая функция имеет доступ к этой
переменной, ссылаясь к ней
по этому имени.
В случаях, когда связь между
функциями осуществляется с
помощью большого числа переменных,
внешние переменные оказы-
ваются более удобными и
эффективными, чем использование
длинных списков аргументов. Как,
однако, отмечалось в главе
1, это соображение следует
использовать с определенной осто-
рожностью, так как оно может плохо
отразиться на структуре
программ и приводить к программам с
большим числом связей по
данным между функциями.
Вторая причина использования
внешних переменных связана
с инициализацией. В частности,
внешние массивы могут быть
инициализированы а автоматические
нет. Мы рассмотрим вопрос
об инициализации в конце этой
главы.
Третья причина использования
внешних переменных обуслов-
лена их областью действия и
временем существования. Автома-
тические переменные являются
внутренними по отношению к фун-
кциям; они возникают при входе в
функцию и исчезают при вы-
ходе из нее. Внешние переменные,
напротив, существуют посто-
янно. Они не появляютя и не
исчезают, так что могут сохра-
нять свои значения в период от
одного обращения к функции до
другого. В силу этого, если две
функции используют некоторые
общие данные, причем ни одна из них
не обращается к другой ,
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
|