Еще терминология (C, а не C++): прототип функции объявляет типы ее аргументов. В противном случае функция не имеет прототипа.
void f(); // Declaration, but not a prototype
void f(void); // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype
Объявления, которые не являются прототипами, являются пережитком C до ANSI, со времен K&R C. Единственная причина использовать объявление в старом стиле — поддерживать двоичную совместимость со старым кодом. Например, в Gtk 2 есть объявление функции без прототипа — оно там случайно, но его нельзя удалить, не нарушив двоичный код. Стандартные комментарии C99:
6.11.6 Деклараторы функций
Использование деклараторов функций с пустыми скобками (не деклараторов типов параметров в формате прототипа) является устаревшей функцией.
Рекомендация: я предлагаю компилировать весь код C в GCC/Clang с -Wstrict-prototypes
и -Wmissing-prototypes
в дополнение к обычному -Wall -Wextra
.
Что случается
void f(); // declaration
void f(int a, int b, float c) { } // ERROR
Объявление не соответствует телу функции! На самом деле это ошибка времени компиляции, потому что у вас не может быть аргумента float
в функции без прототипа. Причина, по которой вы не можете использовать float
в непрототипной функции, заключается в том, что когда вы вызываете такую функцию, все аргументы продвигаются, используя определенные продвижения по умолчанию. Вот фиксированный пример:
void f();
void g()
{
char a;
int b;
float c;
f(a, b, c);
}
В этой программе a
повышается до int
1, а c
повышается до double
. Таким образом, определение для f()
должно быть:
void f(int a, int b, double c)
{
...
}
См. C99 6.7.6 параграф 15,
Если один тип имеет список типов параметров, а другой тип задается декларатором функции, который не является частью определения функции и содержит пустой список идентификаторов, список параметров не должен иметь знака конца с многоточием, а тип каждого параметра должен быть совместимым с типом, полученным в результате применения продвижения аргумента по умолчанию.
Ответ 1
Что происходит во время компиляции в случаях 1 и 2, когда мы вызываем f
с правильными аргументами, неправильными аргументами и вообще без аргументов? Что происходит во время выполнения?
Когда вы вызываете f()
, параметры продвигаются с использованием продвижения по умолчанию. Если продвигаемые типы соответствуют фактическим типам параметров для f()
, то все в порядке. Если они не совпадают, он вероятно скомпилируется, но вы определенно получите неопределенное поведение.
«Неопределенное поведение» — это спецификация для «мы не даем никаких гарантий относительно того, что произойдет». Может быть, ваша программа рухнет, может быть, она будет нормально работать, может быть, она пригласит ваших родственников на ужин.
Есть два способа получить диагностику во время компиляции. Если у вас есть сложный компилятор с возможностями межмодульного статического анализа, вы, вероятно, получите сообщение об ошибке. Вы также можете получать сообщения для непрототипированных объявлений функций с помощью GCC, используя -Wstrict-prototypes
, который я рекомендую включать во всех ваших проектах (за исключением файлов, использующих Gtk 2).
Ответ 2
Если я объявлю f
с аргументами, но определю без них, будет ли это иметь значение? Должен ли я иметь возможность обращаться к аргументам из тела функции?
Он не должен компилироваться.
Исключения
На самом деле есть два случая, когда аргументы функции могут не совпадать с определением функции.
Можно передать char *
функции, которая ожидает void *
, и наоборот.
Допустимо передавать целочисленный тип со знаком в функцию, которая ожидает беззнаковую версию этого типа, или наоборот, если значение может быть представлено в обоих типах (т. е. оно не является отрицательным и не выходит за пределы диапазона знаковый тип).
Сноски
1: возможно, что char
становится unsigned int
, но это очень редко.
person
Dietrich Epp
schedule
10.11.2012
void f();
совпадает сvoid f(void);
. В одном вы неявно говорите об отсутствии аргументов (компилятор делает вывод об этом), а в другом вы явно говорите об отсутствии аргументов. Также будет лучше, если вы попробуете эти примеры самостоятельно, чтобы вам было удобнее использовать компилятор позже. - person yeyo   schedule 10.11.2012void f();
не является прототипом (это объявление, которое не является прототипом). - person M.M   schedule 20.08.2014