Опубликован: 05.07.2006 | Уровень: для всех | Доступ: платный
Лекция 5:

Функции и структура программ

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 ' и показатель экспоненты, возможно со знаком.

Ярослав Воробей
Ярослав Воробей
Россия
Дмитрий Левин
Дмитрий Левин
Россия