Объявление функции перед ее использованием, имеет ли это значение? - PullRequest
2 голосов
/ 10 февраля 2020

В чем разница между этими двумя кодами тела функции? (Первый код взят из C книги по языку программирования)

В чем разница между объявлением функции и ее использованием по сравнению с ее непосредственным использованием без предварительного объявления?

int atoi(char s[]) {
    double atof(char s[]);
    return (int) atof(s);
}

int atoi(char s[]) {
    return (int) atof(s);
}

1 Ответ

3 голосов
/ 10 февраля 2020

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

В следующем обсуждении делается предположение, что функция atof() не объявлена ​​или не определена любым способом (включите файл, et c.) перед его использованием в примерах.

В первом примере у вас есть явное объявление функции atof(), которая позволяет компилятору выполнять некоторые проверки, которые он используется правильно.

int atoi(char s[]) {
    double atof(char s[]);   // atof is a function that takes a character array and returns a double value
    return (int) atof(s);
}

Кстати, я бы объявил это как extern double atof(char s[]);, просто чтобы дать понять читателю, что это внешняя функция.

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

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

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

int atoi(char s[]) {
    return (int) atof(s);
}

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

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

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

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

Эксперимент

Поэкспериментируя с Visual Studio 2015, после объявления функции, даже если внутри области действия функции, компилятор запоминает объявление и использует это объявление для проверки любых других применений, следующих за в исходном файле. Также, если функция используется без объявления, компилятор генерирует объявление, которое он затем проверяет для любых других мест, где используется функция.

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

Например, в следующем исходном коде объявление функции atof1(), объявленной в первой функции, имеет вид используется для проверки использования функции atof1() во второй функции. Кроме того, компилятор создает объявление для функции atof2(), когда сталкивается с используемой функцией, и оно не объявляется, и это созданное объявление используется для проверки правильности любого другого случая использования функции atof2().

int xatoi(char s[]) {
    extern double atof1(char s[]);   // atof is a function that takes a character array and returns a double value
    extern double jj;

    jj = atof1(s);      // line 106
    jj = atof2(s);      // line 107, undeclared undefined function, compiler creates declaration, assumes function returns int
    return (int)jj;     // line 108
}

int xato2(char s[])
{
    int kk;             // line 113
    jj = atof1(s);      // line 114, variable jj is declared in function above but not in this one.
    kk = atof1(s);      // line 115, double value returned by atof1() is converted to int
    kk = atof2(s);      // line 116, compiler uses created declaration to check and atof2() is assumed to return an int
    jj = atof1(s, 3);   // line 117, this use of atof1() does not match the declaration of atof1() in function xatoi() above.
    return (int)jj;     // line 118
}

Приведенный выше код генерирует следующие предупреждения и ошибки в Visual Studio 2015.

1>mldmodd.c(107): warning C4013: 'atof2' undefined; assuming extern returning int
1>mldmodd.c(114): error C2065: 'jj': undeclared identifier
1>mldmodd.c(114): warning C4244: '=': conversion from 'double' to 'int', possible loss of data
1>mldmodd.c(115): warning C4244: '=': conversion from 'double' to 'int', possible loss of data
1>mldmodd.c(117): error C2065: 'jj': undeclared identifier
1>mldmodd.c(117): warning C4020: 'atof1': too many actual parameters
1>mldmodd.c(117): warning C4244: '=': conversion from 'double' to 'int', possible loss of data
1>mldmodd.c(118): error C2065: 'jj': undeclared identifier
...