Подтвердить что ты не робот

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

Я читаю книгу "Программирование на C" и нашел в главе 10 пример следующего вида:

#include <stdio.h>

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?

4b9b3361

Ответ 1

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

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

void test (int *int_pointer);

int main (void) {

    ...

}

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

Ответ 2

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

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

Ответ 3

Это не idomatic C, но все еще действительный.

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

Ответ 4

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

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

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

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

В практическом плане это ограниченное использование - обычно, если вы не хотите, чтобы функция была видимой, вы помещаете ее в другую единицу перевода (и предпочтительно делаете ее static), но вы, вероятно, можете создать ситуацию, когда вы можете использовать эту возможность для создания системы загрузки модулей, которая не экспортирует исходные объявления ее компонентов или что-то в этом роде (и тот факт, что это не зависит от 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"

Ответ 5

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

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

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