Смекни!
smekni.com

тельно, оно является подходящим индексом для массива NDIGIT.

Выяснение вопроса, является ли данный символ цифрой,

символом пустого промежутка или чем-либо еще, осуществляется

последовательностью операторов

IF (C >= '0' && C <= '9')

++NDIGIT[C-'0'];

ELSE IF(C == ' ' &bsol;!&bsol;! C == '&bsol;N' &bsol;!&bsol;! C == '&bsol;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&bsol;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&bsol;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 должна передавать сигнал о возможном по-