Это означает, что во втором случае вызывающая сторона несет ответственность за то, чтобы указанные аргументы имели правильный тип; неявное преобразование не будет предоставлено (кроме продвижения аргументов по умолчанию). Из раздела 6.5.2.2:
Если выражение, которое обозначает вызываемую функцию, имеет тип, который не включает
прототип, целочисленные продвижения выполняются для каждого аргумента.
...
Если выражение, обозначающее вызываемую функцию, имеет тип, который включает в себя прототип, аргументы неявно преобразуются, как если бы путем присваивания, в типы
соответствующие параметры.
Значит, такой код вызова будет в порядке:
char x = 3;
char y = 7;
max(x, y); // Equivalent to max((int)x, (int)y)
потому что x
и y
повышаются до int
до помещения в стек.
Однако код, подобный этому, не будет в порядке:
double x = 3.0;
long y = 7;
max(x, y); // Uh-oh
x
и y
будут помещены в стек как double
и long
, но max()
попытается прочитать два int
s, что приведет к неопределенному поведению (на практике необработанные биты будут переинтерпретированы).
Это одна из причин не использовать вторую форму; единственная причина, по которой он входит в стандарт, заключается в обеспечении обратной совместимости с (чрезвычайно) устаревшим кодом. Если вы используете GCC, вы можете применить это с помощью флага -Wold-style-definition
; Я надеюсь, что другие компиляторы предложат что-то эквивалентное.