Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 291 292 293 294 295 296 297 298 299 ... 337
*/

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

void print_int(int);            /* печать целого числа int */

void print_string(const char*); /* печать строки */

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

27.2.2. Проверка типов аргументов функций

Рассмотрим следующий пример:

int main()

{

  f(2);

}

 

 Компилятор языка С допускает такой код: вы не обязаны объявлять функции до их использования (хотя можете и должны). Определение функции f() может находиться где-то в другом месте. Кроме того, функция f() может находиться в другом модуле компиляции, в противном случае редактор связей сообщит об ошибке.

К сожалению, это определение в другом исходном файле может выглядеть следующим образом:

/* other_file.c: */

int f(char* p)

{

  int r = 0;

  while (*p++) r++;

  return r;

}

Редактор связей не сообщит об этой ошибке. Вместо этого вы получите ошибку на этапе выполнения программы или случайный результат.

 

 Как решить эту проблему? На практике программисты придерживаются согласованного использования заголовочных файлов. Если все функции, которые вы вызываете или определяете, объявлены в заголовке, поставленном в соответствующее место программы с помощью директивы #include, будет включен механизм проверки типов. Однако в больших программах на это трудно рассчитывать. Вследствие этого в большинстве компиляторов языка С существуют опции, предусматривающие выдачу предупреждений о вызовах необъявленных функций: воспользуйтесь ими. Кроме того, с первых дней существования языка C появились программы, с помощью которых можно выявлять все возможные проблемы, связанные непротиворечивостью типов. Обычно они называются lint. Используйте их для любой нетривиальной программы на языке С. Вы обнаружите, что программы lint подталкивают вас использовать язык С как подмножество языка С++. Одно из наблюдений, приведших к разработке языка С++, состояло в том, что компилятор мог легко проверять многое (но не все), что могли проверять программы lint.

Вы можете попросить включить проверку аргументов функций в языке С. Для этого достаточно объявить функцию с заданными типами аргументов (точно так же, как в языке С++). Такое объявление называется прототипом функции (function prototype). Тем не менее следует избегать объявлений, не задающих аргументы; они не являются прототипами функций и не включают механизм проверки типов.

int g(double); /* прототип — как в языке С ++ */

int h();       /* не прототип — типы аргументов не указаны */

void my_fct()

{

  g();       /* ошибка: пропущен аргумент */

  g("asdf"); /* ошибка: неправильный тип аргумента */

  g(2);      /* OK: 2 преобразуется в 2.0 */

  g(2,3);    /* ошибка: один аргумент лишний */

  h();       /* Компилятор допускает! Результат непредсказуем */

  h("asdf"); /* Компилятор допускает! Результат непредсказуем */

  h(2);      /* Компилятор допускает! Результат непредсказуем */

  h(2,3);    /* Компилятор допускает! Результат непредсказуем */

}

 

 В объявлении функции h() не указан тип аргумента. Это не означает, что функция h() не получает ни одного аргумента; это значит: принимает любой набор аргументов и надеется, что это набор при вызове окажется правильным. И снова отметим, что хороший компилятор предупредит об этой проблеме, а программа lint перехватит ее.

Существует специальный набор правил, регламентирующих преобразование аргументов, если в области видимости нет прототипа функции. Например, переменные типов char и short преобразуются в переменные типа int, а переменные типа float — в переменные типа double. Если вы хотите знать, скажем, что произойдет с переменной типа long, загляните в хороший учебник по языку С. Наша рекомендация проста: не вызывайте функций, не имеющих прототипов.

Обратите внимание на то, что, хотя компилятор допускает передачу аргументов неправильного типа, например параметр типа char* вместо параметра типа int, использование таких аргументов приводит к ошибкам. Как сказал Деннис Ритчи: “С — это язык программирования со строгим контролем типов и слабой проверкой”.

27.2.3. Определения функций

Можете определять функции точно так же, как в языке С++. Эти определения являются прототипами функций.

double square(double d)

{

  return d*d;

}

void ff()

{

  double x = square(2);       /* OK: переводим 2 в 2.0 и вызываем */

  double y = square();        /* пропущен аргумент */

  double y = square("Hello"); /* ошибка: неправильный тип

                                 аргументов */

 double y = square(2,3);      /* ошибка: слишком много аргументов */

}

Определение функции без аргументов не является прототипом функции.

void f() { /* что-то делает */ }

void g()

{

  f(2);    /* OK в языке C; ошибка в языке C++ */

}

Код

void f();  /* не указан тип аргумента */

означающий, что функция f() может принять любое количество аргументов любого типа, выглядит действительно странно. В ответ на это я изобрел новое обозначение, в котором понятие “ничего” указывалось явным образом с помощью ключевого слова void (void — слово из четырех букв, означающее “ничего”).

void f(void); /* не принимает никаких аргументов */

 

 Впрочем, вскоре я об этом пожалел, потому что эта конструкция выглядит странно и при последовательной проверке типов аргументов является излишней. Что еще хуже, Деннис Ритчи (автор языка C) и Дуг Мак-Илрой (Doug McIlroy) (законодатель мод в Исследовательском центре компьютерных наук в компании Bell Labs (Bell Labs Computer Science Research Center; см. раздел 22.2.5) назвали это решение “отвратительным”. К сожалению, оно стало очень популярным среди программистов, работающих на языке С. Тем не менее не используйте его в программах на языке С++, в которых оно выглядит не только уродливо, но и является совершенно излишним.

 

1 ... 291 292 293 294 295 296 297 298 299 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий