|
Функции и структура программ
4.2. Функции, возвращающие нецелые значения
До сих пор ни одна из наших программ не содержала какого-либо описания типа функции. Дело в том, что по умолчанию функция неявно описывается своим появлением в выражении или операторе, как, например, в
while (getline(line, maxline) > 0)
Если некоторое имя, которое не было описано ранее, появляется в выражении и за ним следует левая круглая скобка, то оно по контексту считается именем некоторой функции. Кроме того, по умолчанию предполагается, что эта функция возвращает значение типа int. Так как в выражениях char преобразуется в int, то нет необходимости описывать функции, возвращающие char. Эти предположения покрывают большинство случаев, включая все приведенные до сих пор примеры.
Но что происходит, если функция должна возвратить значение какого-то другого типа? Многие численные функции, такие как sqrt, sin и cos возвращают double ; другие специальные функции возвращают значения других типов. Чтобы показать, как поступать в этом случае, давайте напишем и используем функцию atof(s), которая преобразует строку s в эквивалентное ей плавающее число двойной точности. функция atof является расширением atoi, варианты которой мы написали в "лекции №2" и "№3" ; она обрабатывает необязательно знак и десятичную точку, а также целую и дробную часть, каждая из которых может как присутствовать, так и отсутствовать./Эта процедура преобразования ввода не очень высокого качества; иначе она бы заняла больше места, чем нам хотелось бы/.
Во-первых, сама atof должна описывать тип возвращаемого ею значения, поскольку он отличен от int. Так как в выражениях тип float преобразуется в double, то нет никакого смысла в том, чтобы atof возвращала float ; мы можем с равным успехом воспользоваться дополнительной точностью, так что мы полагаем, что возвращаемое значение типа double. Имя типа должно стоять перед именем функции, как показывается ниже:
double atof(s) /* convert string s to double */ char s[]; { double val, power; int i, sign; for(i=0; s[i]==' ' || s[i]=='\n' || s[i]=='\t'; i++) ; /* skip white space */ sign = 1; if (s[i] == '+' || s[i] == '-') /* sign */ 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); }
Вторым, но столь же важным, является то, что вызывающая функция должна объявить о том, что atof возвращает значение, отличное от int типа. Такое объявление демонстрируется на примере следующего примитивного настольного калькулятора /едва пригодного для подведения баланса в чековой книжке/, который считывает по одному числу на строку, причем это число может иметь знак, и складывает все числа, печатая сумму после каждого ввода.
#define maxline 100 main() /* rudimentary desk calkulator */ { double sum, atof(); char line[maxline]; sum = 0; while (getline(line, maxline) > 0) printf("\t%.2f\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(s) /* convert string s to integer */ char s[]; { double atof(); return(atof(s)); }
Обратите внимание на структуру описаний и оператор return. Значение выражения в
return (выражение)
всегда преобразуется к типу функции перед выполнением самого возвращения. Поэтому при появлении в операторе return значение функции atof, имеющее тип double, автоматически преобразуется в int, поскольку функция atoi возвращает int. (Как обсуждалось в "лекции №2" , преобразование значения с плавающей точкой к типу int осуществляется посредством отбрасывания дробной части).
Упражнение 4-2
Расширьте atof таким образом, чтобы она могла работать с числами вида
123.45е-6
где за числом с плавающей точкой может следовать ' e ' и показатель экспоненты, возможно со знаком.