Книга: Язык программирования Си. Издание 3-е, исправленное

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

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

В предыдущих примерах функции либо вообще не возвращали результирующих значений (void), либо возвращали значения типа int. А как быть, когда результат функции должен иметь другой тип? Многие вычислительные функции, как, например, sqrt, sin и cos, возвращают значения типа double; другие специальные функции могут выдавать значения еще каких-то типов. Чтобы проиллюстрировать, каким образом функция может возвратить нецелое значение, напишем функцию atof(s), которая переводит строку s в соответствующее число с плавающей точкой двойной точности. Функция atof представляет собой расширение функции atoi, две версии которой были рассмотрены в главах 2 и 3. Она имеет дело со знаком (которого может и не быть), с десятичной точкой, а также с целой и дробной частями, одна из которых может отсутствовать. Наша версия не является высококачественной программой преобразования вводимых чисел; такая программа потребовала бы заметно больше памяти. Функция atof входит в стандартную библиотеку программ: ее описание содержится в заголовочном файле ‹stdlib.h›.

Прежде всего отметим, что объявлять тип возвращаемого значения должна сама atof, так как этот тип не есть int. Указатель типа задается перед именем функции.

#include ‹ctype.h›
/*atof: преобразование строки s в double */
double atof (char s[])
{
 double val, power;
 int i, sign;
 for (i = 0; isspace(s[i]); i++)
  ; /* игнорирование левых символов-разделителей */
 sign = (s[i] == '-') ? -1 : 1;
 if (s[i] == '+' || s[i] == '-')
  i++;
 for (val = 0.0; isdigit(s[i]); i++)
  val = 10.0 * val + (s[i] - '0');
 if (s[i] == '.')
  i++;
 for (power = 1.0; isdigit(s[i]); i++) {
  val = 10.0 * val + (s.[i] - '0');
  power *= 10.0;
 }
 return sign * val / power;
}

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

#include ‹stdio.h›
#define MAXLINE 100
/* примитивный калькулятор */
main()
{
 double sum, atof (char[]);
 char line[MAXLINE];
 int getline (char line[], int max);
 sum = 0;
 while (getline(line, MAXLINE) › 0)
  printf ("t%gn", sum += atof(line));
 return 0;
}

В объявлении

double sum, atof (char[]);

говорится, что sum - переменная типа double, a atof - функция, которая принимает один аргумент типа char[] и возвращает результат типа double.

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

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

sum += atof(line);

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

double atof();

то и в этом случае считается, что ничего об аргументах atof не известно, и все проверки на соответствие ее параметров будут выключены. Предполагается, что такая специальная интерпретация пустого списка позволит новым компиляторам транслировать старые Си-программы. Но в новых программах пользоваться этим - не очень хорошая идея. Если у функции есть аргументы, опишите их, если их нет, используйте слово void.

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

/* atoi: преобразование строки s в int с помощью atof */
int atoi (char s[])
{
 double atof (char s[]);
 return (int) atof (s);
}

Обратите внимание на вид объявления и инструкции return. Значение выражения в

return выражение;

перед тем, как оно будет возвращено в качестве результата, приводится к типу функции. Следовательно, поскольку функция atoi возвращает значение int, результат вычисления atof типа double в инструкции return автоматически преобразуется в тип int. При преобразовании возможна потеря информации, и некоторые компиляторы предупреждают об этом. Оператор приведения явно указывает на необходимость преобразования типа и подавляет любое предупреждающее сообщение.

Упражнение 4.2. Дополните функцию atof таким образом, чтобы она справлялась с числами вида

123.45e-6

в которых после мантиссы может стоять e (или E) с последующим порядком (быть может, со знаком).

Оглавление книги


Генерация: 1.910. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз