тельно, оно является подходящим индексом для массива NDIGIT.
Выяснение вопроса, является ли данный символ цифрой,
символом пустого промежутка или чем-либо еще, осуществляется
последовательностью операторов
IF (C >= '0' && C <= '9')
++NDIGIT[C-'0'];
ELSE IF(C == ' ' \!\! C == '\N' \!\! C == '\T')
++NWHITE;
ELSE
++NOTHER;
Конструкция
IF (условие)
оператор
ELSE IF (условие)
оператор
ELSE
оператор
часто встречаются в программах как средство выражения ситуа-
ций, в которых осуществляется выбор одного из нескольких
возможных решений.
Программа просто движется сверху вниз до тех пор, пока
не удовлетворится какое-нибудь условие; тогда выполняется
соответствующий 'оператор', и вся конструкция завершается.
/Конечно, 'оператор' может состоять из нескольких операто-
ров, заключенных в фигурные скобки/. Если ни одно из условий
не удовлетворяется, то выполняется 'оператор', стоящий после
заключительного ELSE, если оно присутствует. Если последнеE
ELSE и соответствующий 'оператор' опущены (как в программе
подсчета слов), то никаких действий не производится. Между
начальным IF и конечным ELSE может помещаться произвольное
количество групп
ELSE IF (условие)
оператор
С точки зрения стиля целесообразно записывать эту конст-рукцию так, как мы показали, с тем чтобы длинные выражения не залезали за правый край страницы.
Оператор SWITCH (переключатель), который рассматривается
в главе 3, представляет другую возможность для записи раз-
ветвления на несколько вариантов. этот оператор особенно
удобен, когда проверяемое выражение является либо просто не-
которым целым, либо символьным выражением, совпадающим с од-
ной из некоторого набора констант. Версия этой программы,
использующая оператор SWITCH, будет для сравнения приведена
в главе 3.
· 29 -
Упражнение 1-12.
Напишите программу, печатающую гистограмму длин слов из
файла ввода. Самое легкое - начертить гистограмму горизон-
тально; вертикальная ориентация требует больших усилий.
1.7. Функции.
В языке “C” функции эквивалентны подпрограммам или функ-
циям в фортране или процедурам в PL/1, паскале и т.д. Функ-
ции дают удобный способ заключения некоторой части вычисле-
ний в черный ящик, который в дальнейшем можно использовать,
не интересуясь его внутренним содержанием. Использование
функций является фактически единственным способом справиться
с потенциальной сложностью больших программ. Если функции
организованы должным образом, то можно игнорировать то, как
делается работа; достаточно знание того, что делается. Язык
“C” разработан таким образом, чтобы сделать использование
функций легким, удобным и эффективным. Вам будут часто вст-
речаться функции длиной всего в несколько строчек, вызывае-
мые только один раз, и они используются только потому, что
это проясняет некоторую часть программы.
До сих пор мы использовали только предоставленные нам
функции типа PRINTF, GETCHAR и PUTCHAR; теперь пора написать
несколько наших собственных. так как в “C” нет операции воз-
ведения в степень, подобной операции ** в фортране или PL/1,
давайте проиллюстрируем механику определения функции на при-
мере функции POWER(M,N), возводящей целое м в целую положи-
тельную степень N. Так значение POWER(2,5) равно 32. Конеч-
но, эта функция не выполняет всей работы операции **, пос-
кольку она действует только с положительными степенями не-
больших чисел, но лучше не создавать дополнительных затруд-
нений, смешивая несколько различных вопросов.
Ниже приводится функция POWER и использующая ее основная
программа, так что вы можете видеть целиком всю структуру.
MAIN() /* TEST POWER FUNCTION */
{
INT I;
FOR(I = 0; I < 10; ++I)
PRINTF(“%D %D %D\N”,I,POWER(2,I),POWER(-3,I));
}
POWER(X,N) /* RAISE X N-TH POWER; N > 0 */
INT X,N;
{
INT I, P;
P = 1;
FOR (I =1; I <= N; ++I)
P = P * X;
RETURN (P);
}
· 30 -
Все функции имеют одинаковый вид:
имя (список аргументов, если они имеются)
описание аргументов, если они имеются
{
описания
операторы
}
Эти функции могут быть записаны в любом порядке и нахо-
диться в одном или двух исходных файлах. Конечно, если ис-
ходная программа размещается в двух файлах, вам придется
дать больше указаний при компиляции и загрузке, чем если бы
она находилась в одном, но это дело операционной системы, а
не атрибут языка. В данный момент, для того чтобы все полу-
ченные сведения о прогоне “C”- программ, не изменились в
дальнейшем, мы будем предполагать, что обе функции находятся
в одном и том же файле.
Функция POWER вызывается дважды в строке
PRINTF(“%D %D %D\N”,I,POWER(2,I),POWER(-3,I));
при каждом обращении функция POWER, получив два аргумента,
вазвращает целое значение, которое печатается в заданном
формате. В выражениях POWER(2,I) является точно таким же це-
лым, как 2 и I. /Не все функции выдают целое значение; мы
займемся этим вопросом в главе 4/.
Аргументы функции POWER должны быть описаны соответству-
ющим образом, так как их типы известны. Это сделано в строке
INT X,N;
которая следует за именем функции.
Описания аргументов помещаются между списком аргументов
и открывающейся левой фигурной скобкой; каждое описание за-
канчивается точкой с запятой. Имена, использованные для ар-
гументов функции POWER, являются чисто локальными и недос-
тупны никаким другим функциям: другие процедуры могут ис-
пользовать те же самые имена без возникновения конфликта.
Это верно и для переменных I и P; I в функции POWER никак не
связано с I в функции MAIN.
Значение, вычисленное функцией POWER, передаются в MAIN
с помощью оператора RETURN, точно такого же, как в PL/1.
внутри круглых скобок можно написать любое выражение. Функ-
ция не обязана возвращать какое-либо значение; оператор
RETURN, не содержащий никакого выражения, приводит к такой
же передаче управления, как “сваливание на конец” функции
при достижении конечной правой фигурной скобки, но при этом
в вызывающую функцию не возвращается никакого полезного зна-
чения.
Упражнение 1-13.
Апишите программу преобразования прописных букв из айла
ввода в строчные, используя при этом функцию OWER©, кото-
рая возвращает значение 'C', если C'- не буква, и значение
соответствующей строчной уквы, если 'C'-буква.
· 31 -
1.8. Аргументы - вызов по значению.
Один аспект в “C” может оказаться непривычным для прог-
раммистов, которые использовали другие языки, в частности,
фортран и PL/1. в языке “C” все аргументы функций передаются
“по значению”. это означает, что вызванная функция получает
значения своих аргументов с помощью временных переменных
/фактически через стек/, а не их адреса. Это приводит к не-
которым особенностям, отличным от тех, с которыми мы сталки-
вались в языках типа фортрана и PL/1, использующих “вызов по
ссылке “, где вызванная процедура работает с адресом аргу-
мента, а не с его значением.
Главное отличие состоит в том, что в “C” вызванная функ-
ция не может изменить переменную из вызывающей функции; она
может менять только свою собственную временную копию.
Вызов по значению, однако, не помеха, а весьма ценное
качество. Оно обычно приводит к более компактным программам,
содержащим меньше не относящихся к делу переменных, потому
что с аргументами можно обращаться как с удобно инициализи-
рованными локальными перемнными вызванной процедуры. Вот,
например, вариант функции POWER использующей это обстоятель-
ство
POWER(X,N) /* RAISE X N-TH POWER; N > 0;
VERSION 2 */
INT X,N;
{
INT P;
FOR (P = 1; N > 0; --N)
P = P * X;
RETURN (P);
}
Аргумент N используется как временная переменная; из не-
го вычитается единица до тех пор, пока он не станет нулем.
Переменная I здесь больше не нужна. чтобы ни происходило с N
внутри POWER это никак не влияет на аргумент, с которым пер-
воначально обратились к функции POWER.
При необходимости все же можно добиться, чтобы функция
изменила переменную из вызывающей программы. Эта программа
должна обеспечить установление адреса переменной /техничес-
ки, через указатель на переменную/, а в вызываемой функции
надо описать соответствующий аргумент как указатель и ссы-
латься к фактической переменной косвенно через него. Мы рас-
смотрим это подробно в главе 5.
Когда в качестве аргумента выступает имя массива, то
фактическим значением, передаваемым функции, является адрес
начала массива. /Здесь нет никакого копирования элементов
массива/. С помощью индексации и адреса начала функция может
найти и изменить любой элемент массива. Это - тема следующе-
го раздела.
· 32 -
1.9. Массивы символов.
По-видимому самым общим типом массива в “C” является
массив символов. Чтобы проиллюстрировать использование мас-
сивов символов и обрабатывающих их функций, давайте напишем
программу, которая читает набор строк и печатает самую длин-
ную из них. Основная схема программы достаточно проста:
WHILE (имеется еще строка)
IF (эта строка длиннее самой длинной из
предыдущих)
запомнить эту строку и ее длину
напечатать самую длинную строку
По этой схеме ясно, что программа естественным образом
распадается на несколько частей. Одна часть читает новую
строку, другая проверяет ее, третья запоминает, а остальные
части программы управляют этим процессом.
Поскольку все так прекрасно делится, было бы хорошо и
написать программу соответсвующим образом. Давайте сначала
напишем отдельную функцию GETLINE, которая будет извлекать
следующую строку из файла ввода; это - обобщение функции
GETCHAR. мы попытаемся сделать эту функцию по возможности
более гибкой, чтобы она была полезной и в других ситуациях.
Как минимум GETLINE должна передавать сигнал о возможном по-