основыкнигиwin/dos*nixготовоелирикагостиФОРУМПОИСК
Б.В. Керниган, Д.М. Ричи -- Язык C

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

ПРЕДЫДУЩАЯ ЧАСТЬ
4.1. Основные сведения
СОДЕРЖАНИЕ СЛЕДУЮЩАЯ ЧАСТЬ
4.3. Еще об аргументах функций