Объявление функции внутри функции – почему?

Я читаю книгу «Программирование на C» и нашел в главе 10 пример:

#include  void test (int  *int_pointer) {      *int_pointer = 100; } int main (void) {      void test (int  *int_pointer);      int  i = 50, *p = &i;      printf ("Before the call to test i = %i\n", i);      test (p);      printf ("After the call to test i = %i\n", i);      return 0; } 

Я понимаю пример, но я не понимаю строку void test (int *int_pointer); внутри main . Почему я снова определяю подпись test ? Это идиоматический C?

Это определенно не идиоматический C, несмотря на то, что он полностью действителен (множественные декларации в порядке, множественных определений нет). Это не нужно, поэтому код будет работать без него.

Если вообще, возможно, автор хотел сделать

 void test (int *int_pointer); int main (void) { ... } 

в случае, если определение функции было задано после main () .

void test (int *int_pointer); это просто декларация (или прототип) test функции. Нет необходимости в этом объявлении в main потому что у вас уже есть определение функции до main .

Если определение test было после main тогда было бы целесообразно поставить там его объявление, чтобы компилятор знал о типе возвращаемого значения, количестве аргументов и аргументах типов test перед его вызовом.

Это не идома C, но все же действительный.

Строка – это объявление test функции, а не определение. Функция не может быть определена несколько раз, но она действительна для нескольких объявлений.

Это совершенно идиоматический C, и на самом деле он имеет (ограниченное) практическое применение – хотя и не тот, который демонстрируется в этом примере.

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

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

Это не имеет смысла в случае примера, потому что определение test также вносит его в сферу охвата для всех следующих тел, но если test был определен в другой единицы перевода или даже если он был определен только в самом низу этого ТУ, скрытие декларации внутри main защитит любые другие функции, определенные впоследствии, от возможности видеть свое имя в своей области.

На практике это ограниченное использование – обычно, если вы не хотите, чтобы функция была видимой, вы помещаете ее в другую единицу перевода (и предпочтительно статируете ее) – но вы, вероятно, можете создать ситуацию, когда вы, возможно, захотите использовать эта способность создавать модульную систему загрузки, которая не экспортирует исходные объявления ее компонентов, или что-то в этом роде (и тот факт, что это не зависит от static / отдельных объектных файлов, потенциально может иметь некоторое отношение к встроенным / -странированные целевые среды, в которых шаг привязки может работать не так, как на ПК, что позволяет вам достичь меры защиты пространства имен в системе сборки, основанной исключительно на #include ).

Пример:

 struct module { void * (* alloc)(size_t); void (* dealloc)(void *); } loaded_module; int main(void) { if (USE_GC) { // dynamically choose the allocator system void * private_malloc_gc(size_t); void private_free_noop(void *); loaded_module = (struct module){ private_malloc_gc, private_free_noop }; } else { void * private_malloc(size_t); void private_free(void *); loaded_module = (struct module){ private_malloc, private_free }; } do_stuff(); //... } // cannot accidentally bypass the module and manually use the wrong dealloc void do_stuff(void) { int * nums = module.alloc(sizeof(int) * 32) //... module.dealloc(nums); } #include "allocator_implementations.c" 

Это не идиоматично; вы обычно видите это в коде, который имеет проблемы с получением своих файлов заголовков по порядку.

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

То, что вы видите здесь, очень плохой стиль (функция должна быть либо статической, либо декларация должна быть взята из стиля заголовка), а также совершенно бессмысленно, потому что компилятор уже может видеть объявление. Поскольку функция находится в том же файле, это не опасно; если декларация и функция не соответствуют компилятору, вам скажут. Я часто видел такие вещи, когда функция находилась в другом файле; это опасно. Если кто-то изменяет функцию, программа, скорее всего, потерпит крах или неправильно.