Yes, they're different; the second is correct, the first one as a whole is wrong. It is so wrong that GCC 5.2.1 refuses to compile it altogether. That it works for you at all is just a fluke:
/* this coupled with */
int function1();
int main() {
/* this */
function1(x, y);
}
/* and this one leads to undefined behaviour */
int function1(int x, float y) {
/* ... */
}
In the code above, the declaration int function1();
does not specify the argument types (it does not have a prototype), which is considered an obsolescent feature in C11 (and C89, C99 for that matter) standard. If that kind of function is called, default argument promotions are done on the arguments: int
is passed as is, but float
is promoted to double
.
As your actual function expects arguments of (int, float)
, not (int, double)
, this will result in undefined behaviour. Even if your function expected a (int, double)
but y
was an integer, or say you called it with function1(0, 0);
instead of function(0, 0.0);
, your program would still have undefined behaviour. Fortunately GCC 5.2.1 notices that the declaration and definition of function1
are conflicting:
% gcc test.c
test.c:9:5: error: conflicting types for ‘function1’
int function1(int x, float y) {
^
test.c:9:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function1(int x, float y) {
^
test.c:1:5: note: previous declaration of ‘function1’ was here
int function1();
^
test.c:12:5: error: conflicting types for ‘function2’
int function2(int x, float y) {
^
test.c:12:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function2(int x, float y) {
^
test.c:2:5: note: previous declaration of ‘function2’ was here
int function2();
^
and the compiler exits with error code, whereas my tcc
compiles it happily, no diagnostics, nothing. It just produces broken code. The same is true of course, if you had the declaration in a header file, and the definition in a different compilation unit that did not include that declaration.
Now, should the compiler not detect this case, at runtime, anything could happen, as expected for undefined behaviour.
For example, suppose a case that the arguments were passed on stack; on 32-bit processors int
and float
could fit in 4 bytes, whereas double
could be 8 bytes; the function call would then push x
as int
and y
as double
even if it was float
- in total the caller would have pushed 12 bytes and the callee would only expect 8.
In another case, suppose you'd call the function with 2 integers. The calling code would then load these into integer registers, but the caller would expect a double in a floating point register. The floating point register could contain a trap value, which, when accessed would kill your program.
What is worst, your program might now behave as expected, thus containing a heisenbug that could cause problems when you recompile the code with a newer version of compiler, or port it to another platform.