Смекни!
smekni.com

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

Пример 5-1. После того, как мы потратили столько усилий на разработку, написание программы в деталях не представляет затруднений. Ниже приводится целиком вся программа, так что вы можете видеть, как соединяются вместе отдельные части. Комбинация символов, по которой производится поиск, выступает пока в качестве символьной строки в аргументе функции index, что не является самым общим механизмом. Мы скоро вернемся к обсуждению вопроса об инициализации символьных массивов и в главе 6 покажем, как сделать комбинацию символов параметром, которому присваивается значение в ходе выполнения программы. Программа также содержит новый вариант функции getline; вам может оказаться полезным сравнить его с вариантом из главы 2.

#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);

}

// Поместить строку в s и возвратить длину

int getline(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);

}

// Вернуть индекс t в s, или -1 в противном случае

int index(char s[], char 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()

{ } ,

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

Программой является просто набор определений отдельных функций. Связь между функциями осуществляется через аргументы и возвращаемые функциями значения (в этом случае); её также можно также осуществлять и через внешние переменные. Функции могут располагаться в исходном файле в любом порядке, а сама исходная программа может размещаться на нескольких файлах, но так, чтобы ни одна функция не расщеплялась.

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

return (выражение) .

Вызывающая функция может игнорировать возвращаемое значение, если она этого пожелает. Более того, после return может не быть вообще никакого выражения; в этом случае в вызывающую программу не передается никакого значения. Управление также возвращется в вызывающую программу без передачи какого-либо значения и в том случае, когда при выполнении мы «проваливаемся» на конец функции, достигая закрывающейся правой фигурной скобки. Eсли функция возвращает значение из одного места и не возвращает никакого значения из другого места, это не является незаконным, но может быть признаком каких-то неприятностей. В любом случае «значением» функции, которая не возвращает значения, несомненно, будет мусор. Отладочная программа Lint проверяет такие ошибки.

Механика компиляции и загрузки «C»-программ, расположенных в нескольких исходных файлах, меняется от системы к системе.

1. В системе Unix, например, эту работу выполняет команда cc, упомянутая в главе 2. Предположим, что три функции находятся в трех различных файлах с именами main.с, getline.c и index.с . Тогда команда:

cc main.c getline.c index.c

компилирует эти три файла, помещает полученный настраиваемый объектный код в файлы main.o, getline.o и index.o и загружает их всех в выполняемый файл, называемый a.out .


Если имеется какая-то ошибка, скажем в main.c, то этот файл можно перекомпилировать отдельно и загрузить вместе с предыдущими объектными файлами по команде

cc main.c getline.o index.o

Команда cc использует соглашение о наименовании: суффиксы «.с» и «.о» для того, чтобы отличить исходные файлы от объектных.

2. В системe Windous XP при использовании оболочки Visual Studio и среды программирования Visual C++ используется прогрессивный «проектный» подход: в состав «рабочего пространства проекта» – Workspace в число исходных файлов – Source Files (см. рис. 1.1) нужно включить только исходные модули с суффиксами «.сpp»: main.сpp, getline.cpp и index.сpp. Решение о том, что нужно перекомпилировать, а что не нужно, – принимает оболочка Visual Studio, причем без дополнительного набора каких-либо команд, как этого, например, требует Unix.

Упражнение 5.1. Составьте программу для функции rindex(s,t), которая возвращает позицию самого правого вхождения t в s, и –1, если s не содержит t.

5.2. Функции, возвращающие нецелые значения

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

while (getline(line, maxline) > 0)

Если некоторое имя, которое не было описано ранее, появляется в выражении и за ним следует левая круглая скобка, то оно по контексту считается именем некоторой функции. Кроме того, по умолчанию предполагается, что эта функция возвращает значение типа int. Так как в выражениях char преобразуется в int, то нет необходимости описывать функции, возвращающие char. Эти предположения покрывают большинство случаев, включая все приведенные до сих пор примеры.

Но что происходит, если функция должна возвратить значение какого-то другого типа? Многие численные функции, такие как sqrt, sin и cos возвращают double – значение с плавающей точкой, с двойной точностью. Другие специальные функции возвращают значения других типов. Чтобы показать, как поступать в этом случае, давайте напишем и используем функцию atоf(s), которая преобразует строку s в эквивалентное ей плавающее число двойной точности.

Пример 5-2. Функция atоf является расширением атоi, варианты которой мы написали в главах 3 и 4; она обрабатывает необязательно знак и десятичную точку, а также целую и дробную часть, каждая из которых может как присутствовать, так и отсутствовать (эта процедура преобразования ввода не очень высокого качества; иначе она бы заняла больше места, чем нам хотелось бы).

Во-первых, сама atоf должна описывать тип возвращаемого ею значения, поскольку он отличен от int. Так как в выражениях тип float преобразуется в double, то нет никакого смысла в том, чтобы atоf возвращала float; мы можем с равным успехом воспользоваться дополнительной точностью, так что мы полагаем, что возвращаемое значение типа double. Имя типа должно стоять перед именем функции, как показывается ниже:

// Преобразование строки s в число с двойной точностью

double atof(char s[])

{

double val, power;

int i, sign;

for(i=0;s[i]==' ' || s[i]=='\n' || s[i]=='\t'; i++)

; // Игнорирование символов-разделителей

sign = 1;

if (s[i] == '+' || s[i] == '-') // Знак числа

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);

}

Пример 5-3. Вторым, но столь же важным, является то, что вызывающая функция должна объявить о том, что atof возвращает значение, отличное от int типа. Такое объявление демонстрируется на примере следующего примитивного настольного калькулятора (едва пригодного для подведения баланса в чековой книжке), который считывает по одному числу на строку, причем это число может иметь знак, и складывает все числа, печатая сумму после каждого ввода.

#define maxline 100

main() // Примитивный калькулятор

{

double sum, atof();

char line[maxline];

sum = 0;

while (getline(line, maxline) > 0)

printf("&bsol;t=%3.2f&bsol;n",sum+=atof(line));

}

Описание:

double sum, atof();

говорит, что sum является переменной типа double , и что atof является функцией, возвращающей значение типа double. Эта мнемоника означает, что значениями как sum, так и atof(...) являются плавающие числа двойной точности.

Если функция atof не будет описана явно в обоих местах, то в «C» предполагается, что она возвращает целое значение, и вы получите бессмысленный ответ. Если сама atof и обращение к ней в main имеют несовместимые типы и находятся в одном и том же файле, то это будет обнаружено компилятором. Но если atof была скомпилирована отдельно /что более вероятно/, то это несоответствие не будет зафиксировано, так что atof будет возвращать значения типа double, с которым main будет обращаться, как с int, что приведет к бессмысленным результатам (программа Lint вылавливает такую ошибку).

Имея функцию atof, мы, в принципе, могли бы с ее помощью написать atoi (преобразование строки в int):

atoi(char s[]) // Преобразование строки в целое число

{

double atof();

return(atof(s));

}

Обратите внимание на структуру описаний и оператор return. Значение выражения в конструкции:

return (выражение)

всегда преобразуется к типу функции перед выполнением самого возвращения. Поэтому при появлении в операторе return значение функции atof, имеющее тип double, автоматически преобразуется в int, поскольку функция atoi возвращает int. (Как обсуждалось в главе 3, преобразование значения с плавающей точкой к типу int осуществляется посредством отбрасывания дробной части).

Упражнение 5-2. Расширьте atof таким образом, чтобы она могла работать с числами вида 123.45е-6 где за числом с плавающей точкой может следовать «е» и показатель «экспоненты», возможно – со знаком.