Смекни!
smekni.com

Методические указания к выполнению контрольных работ по дисциплине "Основы программирования" (стр. 13 из 40)

Мы разбили вводимые символы на двенадцать категорий, и нам удобнее использовать массив для хранения числа появлений каждой цифры, а не десять отдельных переменных. Вот один из вариантов программы:

// Подсчет всех цифр,

// промежутков (пробел, табуляции, новая строка)

// и всех остальных символов

main()

{

int c, i, nwhite, nother;

int ndigit[10];

nwhite = nother = 0;

for (i = 0; i < 10; ++i)

ndigit[i] = 0;

while ((c = getchar()) != eof)

if (c >= '0' && c <= '9')

++ndigit[c-'0'];

else if(c== ' ' || c== '&bsol;n' || c== '&bsol;t')

++nwhite;

else

++nother;

printf("digits =");

for (i = 0; i < 10; ++i)

printf(" %d", ndigit[i]);

printf("&bsol;nwhite space = %d, other = %d&bsol;n",

nwhite, nother);

}

Описание:

int ndigit[10];

объявляет, что ndigit является массивом из десяти целых. В языке «C» индексы массива всегда начинаются с нуля (а не с 1, как в ФОРТРАНЕ или PL/1/, так что элементами массива являются:

ndigit[0], ndigit[1], ..., ndigit[9].

Эта особенность отражена в циклах for , которые инициализируют и печатают массив.

Индекс может быть любым целым выражением, которое, конечно, может включать целые переменные, такие как I , и целые константы.

Эта конкретная программа сильно опирается на свойства символьного представления цифр. Так, например, в программе проверка:

if( c >= '0' && c <= '9')...

определяет, является ли символ в символьной переменной c цифрой, и если это так, то численное значение этой цифры определяется по формуле:

c - '0' .

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

По определению перед проведением арифметических операций, вовлекающих переменные типа char и int, все они преобразуются к типу int, Tак что в арифметических выражениях переменные типа CHAR по существу идентичны переменным типа int. Это вполне естественно и удобно. Например:

c - '0'

- это целое выражение со значением между 0 и 9 в соответствии с тем, какой символ от '0' до '9' хранится в переменной c, и, следовательно, оно является подходящим индексом для массива ndigit.

Выяснение вопроса, является ли данный символ цифрой, символом пустого промежутка или чем-либо еще, осуществляется последовательностью операторов

if (c >= '0' && c <= '9')

++ndigit[c-'0'];

else if(c == ' ' || c == '&bsol;n' || c == '&bsol;t')

++nwhite;

else

++nother;

Конструкция

if (условие)

оператор-1;

else if (условие)

оператор-2;

else

оператор-3;

часто встречаются в программах как средство выражения ситуаций, в которых осуществляется выбор одного из нескольких возможных решений.

Программа просто движется сверху вниз до тех пор, пока не удовлетворится какое-нибудь условие; тогда выполняется соответствующий «оператор»*), и вся конструкция завершается.

Если ни одно из условий не удовлетворяется, то выполняется «оператор», стоящий после заключительного else, если оно присутствует. Если последнее else и соответствующий «оператор» опущены (как в программе подсчета слов), то никаких действий не производится. Между начальным if и конечным else может помещаться произвольное количество групп:

else if (условие)

оператор .

С точки зрения стиля целесообразно записывать эту конструкцию так, как мы показали, с тем, чтобы длинные выражения не залезали за правый край страницы.

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

Упражнение 2-12. Напишите программу, печатающую гистограмму длин слов из файла ввода. Самое легкое – это начертить гистограмму горизонтально; вертикальная ориентация требует больших усилий.

2.7. Функции

В языке «C» функции эквивалентны подпрограммам или функциям в ФОРТРАНЕ или процедурам в PL/1, ПАСКАЛЕ и т.д. Функции дают удобный способ заключения некоторой части вычислений в черный ящик, который в дальнейшем можно использовать, не интересуясь его внутренним содержанием. Использование функций является фактически единственным способом справиться с потенциальной сложностью больших программ. Если функции организованы должным образом, то можно игнорировать то, как делается работа; достаточно знание того, что делается. Язык «C» разработан таким образом, чтобы сделать использование функций легким, удобным и эффективным. Вам будут часто встречаться функции длиной всего в несколько строчек, вызываемые только один раз, и они используются только потому, что это проясняет некоторую часть программы.

До сих пор мы использовали только предоставленные нам функции типа printf, getchar и putchar; теперь пора написать несколько наших собственных. так как в «C» нет операции возведения в степень, подобной операции ** в ФОРТРАНЕ или PL/1.

Давайте проиллюстрируем механику определения функции на примере функции power(m,n), возводящей целое m в целую положительную степень n. Так значение power(2,5) равно 32. Конечно, эта функция не выполняет всей работы операции **, поскольку она действует только с положительными степенями небольших чисел, но лучше не создавать дополнительных затруднений, смешивая несколько различных вопросов.

Пример 2-11. Ниже приводится функция power и использующая ее основная программа, так что вы можете видеть целиком всю структуру.

main() // Испытание функции возведения в степень

{

int i;

for(i = 0; i < 10; ++i)

printf("%d %d %d&bsol;n",i,power(2,i),power(-3,i));

}

power(int x, int n) // Возведение x в степень n, n>0

{

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. (Однако не все функции выдают целое значение; мы займемся этим вопросом в главе 5).

Аргументы функции power должны быть описаны соответствующим образом, так как их типы известны. Это сделано в строке

int x,n;

которая следует за именем функции.

Описания аргументов помещаются между списком аргументов и открывающейся левой фигурной скобкой; каждое описание заканчивается точкой с запятой. Имена, использованные для аргументов функции power, являются чисто локальными и недоступны никаким другим функциям: другие процедуры могут использовать те же самые имена без возникновения конфликта.

Это верно и для переменных i и p; i в функции power никак не связано с i в функции main.

Значение, вычисленное функцией power, передаются в main с помощью оператора return, точно такого же, как в PL/1. Внутри круглых скобок можно написать любое выражение. Функция не обязана возвращать какое-либо значение; оператор return, не содержащий никакого выражения, приводит к такой же передаче управления, как «сваливание на конец» функции при достижении конечной правой фигурной скобки, но при этом в вызывающую функцию не возвращается никакого полезного значения.

Упражнение 2-13. Напишите программу преобразования прописных букв из файла ввода в строчные, используя при этом функцию ower(c), которая возвращает значение – переменную c, если символ в c – не буква, и значение соответствующей строчной буквы, если c – это буква.

2.8. Аргументы: вызов по значению

Один аспект в «C» может оказаться непривычным для программистов, которые использовали другие языки, в частности, ФОРТРАН и PL/1. В языке «C» все аргументы функций передаются «по значению». Это означает, что вызванная функция получает значения своих аргументов с помощью временных переменных (фактически через стек), а не их адреса. Это приводит к некоторым особенностям, отличным от тех, с которыми мы сталкивались в языках типа ФОРТРАНА и PL/1, использующих (вызов по ссылке), где вызванная процедура работает с адресом аргумента, а не с его значением.

Главное отличие состоит в том, что в «C» вызванная функция не может изменить переменную из вызывающей функции; она может менять только свою собственную временную копию.

Вызов по значению, однако, не помеха, а весьма ценное качество. Оно обычно приводит к более компактным программам, содержащим меньше не относящихся к делу переменных, потому что с аргументами можно обращаться как с удобно инициализированными локальными перемнными вызванной процедуры.

Пример 2-12. Рассмотрим вариант функции power, использующей данное обстоятельство

power(int x, int n)

{

int p;

for (p = 1; n > 0; --n)

p = p * x;

return (p);

}

Аргумент n используется как временная переменная; из него вычитается единица до тех пор, пока он не станет нулем. Переменная i здесь больше не нужна. чтобы ни происходило с n внутри power это никак не влияет на аргумент, с которым первоначально обратились к функции power.