Смекни!
smekni.com

определяет, является ли символ в 'C' цифрой, и если это так, то численное значение этой цифры определяется по формуле / C '0'/. Такой способ работает только в том случае, если значения символьных констант '0', '1' и т.д. Положительны, расположены в порядке возрастания и нет ничего, кроме цифр, между константами '0' и '9'. К счастью, это верно для всех общепринятых наборов символов.

По определению перед проведением арифметических операций, вовлекающих переменные типа CHAR и INT, все они преобразуются к типу INT, TAK что в арифметических выражениях переменные типа CHAR по существу идентичны переменным типа INT. Это вполне естественно и удобно; например, C -'0'- это целое выражение со значением между 0 и 9 в соответствии с тем, какой символ от '0' до '9' хранится в 'C', и, следовательно, оно является подходящим индексом для массива 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.

Упражнение 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);

}

Все функции имеют одинаковый вид: имя (список аргументов, если они имеются) описание аргументов, если они имеются

{ описания операторы

}

Эти функции могут быть записаны в любом порядке и находиться в одном или двух исходных файлах. Конечно, если исходная программа размещается в двух файлах, вам придется дать больше указаний при компиляции и загрузке, чем если бы она находилась в одном, но это дело операционной системы, а не атрибут языка. В данный момент, для того чтобы все полученные сведения о прогоне “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'-буква.

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.

Когда в качестве аргумента выступает имя массива, то фактическим значением, передаваемым функции, является адрес начала массива. /Здесь нет никакого копирования элементов массива/. С помощью индексации и адреса начала функция может найти и изменить любой элемент массива. Это - тема следующего раздела.

1.9. Массивы символов.

По-видимому самым общим типом массива в “C” является массив символов. Чтобы проиллюстрировать использование массивов символов и обрабатывающих их функций, давайте напишем программу, которая читает набор строк и печатает самую длинную из них. Основная схема программы достаточно проста:

WHILE (имеется еще строка) IF (эта строка длиннее самой длинной из предыдущих) запомнить эту строку и ее длину напечатать самую длинную строку По этой схеме ясно, что программа естественным образом распадается на несколько частей. Одна часть читает новую строку, другая проверяет ее, третья запоминает, а остальные части программы управляют этим процессом.

Поскольку все так прекрасно делится, было бы хорошо и написать программу соответсвующим образом. Давайте сначала напишем отдельную функцию GETLINE, которая будет извлекать следующую строку из файла ввода; это - обобщение функции GETCHAR. мы попытаемся сделать эту функцию по возможности более гибкой, чтобы она была полезной и в других ситуациях.