The C standard states that, for a function definition, if the declarator includes an identifier list, the types of the parameters shall be declared in a following declaration list. Apparently this makes a difference.
extern int max(int a, int b)
{
return a > b ? a : b;
}
extern int max(a, b)
int a, b;
{
return a > b ? a : b;
}
Here int a, b; is the declaration list for the parameters. The
difference between these two definitions is that the first form acts
as a prototype declaration that forces conversion of the arguments of
subsequent calls to the function, whereas the second form does not.
What does this mean for the programmer and does it affect the code the compiler produces?
It means that in the second case, it's the responsibility of the caller to ensure that the arguments provided are of the correct type; no implicit conversion will be provided (other than the default argument promotions). From section 6.5.2.2:
If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument.
...
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the
corresponding parameters.
So calling code like this will be ok:
char x = 3;
char y = 7;
max(x, y); // Equivalent to max((int)x, (int)y)
because x and y are promoted to int before being placed on the stack.
However, code like this will not be ok:
double x = 3.0;
long y = 7;
max(x, y); // Uh-oh
x and y will be placed on the stack as double and long, but max() will attempt to read two ints, which will result in undefined behaviour (in practice, the raw bits will be reinterpreted).
This is one reason not to use the second form; the only reason it's in the standard is to provide backward compatibility with (extremely) legacy code. If you're using GCC, you can enforce this by using the -Wold-style-definition flag; I would hope that other compilers would offer something equivalent.
Related
In this example i'm getting a Conflicting types error as expected:
#include <stdio.h>
int a(int b);
int a(int *b){
return 6;
}
int main(){
return 0;
}
But not in this example:
#include <stdio.h>
int a(int b);
int a(){
return 6;
}
int main(){
return 0;
}
Why the second example is compiling fine although the declaration and definition and of a are still different?
Overview and History
Due to the history of how the C language developed, int a() does not mean “a takes no arguments and returns an int.” It means “a takes unspecified arguments and returns an int.”
Since “unspecified arguments” is nominally compatible with “one argument that is an int,” the compiler does not give an error for conflicting types. Due to further rules in C (see “Function Compatibility” below), the types are incompatible, but the compiler is not required to diagnose this.
Originally in C, functions were declared with a parameter list of (), and it was up to the caller to provide the correct types. (In addition, arguments were “promoted”; char arguments were converted to int, and so on, but this is a separate issue.) Where a function was defined, it was defined with names of parameters, such as int a(b), and the declarations of those parameters followed, as in:
int a()
int b;
{
return 6*b;
}
But that was only for the definition. The declaration did not have those declarations of the parameter types.
Later, to improve the type information about functions, C added grammar for full declarations of functions including parameter types, such as int a(int b). However, since the old grammar was already using int a() to mean “unspecified parameters,” the language had to keep that meaning to support old code.
Instead, a special form was designated to mean “no parameters,” and that is to put void by itself in the parameter list. int a(void) means “a is a function taking no parameters and returning int.” So, if you declare a function as int a(void); and then define it with int a(int b) { … }, the compiler will give you an error message.
Function Compatibility
One of the rules for function types to be compatible in C 2018 6.7.6.3 15 says:
… If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters,…
The declaration int a(int b); has a parameter type list with one parameter.
This definition:
int a(){
return 6;
}
has an empty identifier list and, between the int a() and the {, defines no parameters. So they do not agree in the number of parameters. However, the compiler is not required to diagnose this incompatibility.
Because in C (but not C++), a() declares a function with an unspecified number of parameters. Use a(void) instead.
See C function with no parameters behavior
Second example:
int a(){
a is a function taking unspecified number of parameters. So any number is fine and thus no error.
It is a compiler defect.
According to the C Standard (6.7.6.3 Function declarators (including prototypes))
...If one type has a parameter type list and the other type is specified
by a function definition that contains a (possibly empty) identifier
list, both shall agree in the number of parameters, and the type of
each prototype parameter shall be compatible with the type that
results from the application of the default argument promotions to the
type of the corresponding identifier
So as in these declarations
int a(int b);
int a(){
return 6;
}
the number of parameters differs for the functions the compiler shall issue a message because the name a is used to declare two different functions.
For example running this code using gcc 8.3 you can get the following errors
prog.c: In function ‘a’:
prog.c:5:1: error: number of arguments doesn’t match prototype
int a(){
^~~
prog.c:3:5: error: prototype declaration
int a(int b);
^
What happens
during the compilation and Linking process if the argument types are omitted(considering that the defined function takes arguments)?
Will the compiler just mark the prototype with missing argument as a syntax error?
Edit
so I found out it builds and runs
#include<stdio.h>
float addf(a,b); // even without a,b it runs
int main(){
float input1 = 10.0;
float input2 = 20.0;
printf("%f + %f = %f", input1, input2, addf(input1, input2) );
getchar();
return 0;
}
float addf(float a, float b){
return a + b;
}
result : 10.000000 + 20.000000 = 2.562500
Any idea why is it executed this way?
The phrasing in the question is in error. You cannot omit the types in a function prototype. If you omit the types in function definition, declaration, it doesn't have a prototype. Only the declaration, definition that contains function types (be it just void) has a prototype.
When you do omit the types in a function definition, you get the old style* declaration-list. You must specify types for the parameters before the function body, an example of which can be seen here. The caller cannot know how to call this correctly, and the compiler cannot check. Scalar types are subject to default argument promotions, just like the variable-argument part of the prototyped functions.
As for the code in your question, it is not standards-compliant - in fact, there are 2 grave errors:
float addf(a,b); // even without a,b it runs
Is an incorrect declaration, that is not allowed by C - you must write
float addf(); // empty parentheses
and b, the actual parameters must be of type double, because the default argument promotions will convert them.
I have a simple code, where my functions are declared before main function like that:
int function1();
int function2();
int main() {
/* ... */
function1(x,y);
function2(x,y);
/* .... */
}
int function1(int x, float y) { /* ... */ }
int function2(int x, float y) { /* ... */ }
And after my main function I have definitions of functions:
Is there some difference, when I declare functions before main like this?
int function1(int x, float y);
int function2(int x, float y);
int main() {
/* ... */
function1(x,y);
function2(x,y);
/* .... */
}
int function1(int x, float y) { /* ... */ }
int function2(int x, float y) { /* ... */ }
Yes, they are different.
In the first example, you are just telling the compiler about the name and return type of the function and nothing of its expected arguments.
In the second example you are telling the compiler the full signature of the functions, both return type and expected arguments, prior to calling them.
The second form is pretty much universally better as it helps you compiler do a better job warning you when you have the wrong type or number of arguments when calling the function.
Also note int function() in C is a function that can accept any arguments, not a function that accepts no arguments. For that you need an explicit void, i.e int function(void). This mostly trips up those coming to C from C++.
See also:
Why does a function with no parameters (compared to the actual function definition) compile?
To demonstrate why the first, antiquated form is bad in modern C, the following program compiles without warning with gcc -Wall -ansi -pedantic or gcc -Wall -std=c11.
#include<stdio.h>
int foo();
int main(int argc, char**argv)
{
printf("%d\n", foo(100));
printf("%d\n", foo(100,"bar"));
printf("%d\n", foo(100,'a', NULL));
return 0;
}
int foo(int x, int y)
{
return 10;
}
UPDATE: M&M brought to my attention that my example uses int not float for the functions. I think we can all agree that declaring int function1() is bad form, but my statement that this declaration accepts any arguments is not quite correct. See Vlad's answer for relevant spec section explaining why that is the case.
The difference is that then there is a function prototype as in the second code snippet then the compiler checks that the number and types of arguments correspond to the number and types of the parameters. The compiler can issue an error at the compilation-time if it will find an inconsistence.
If there is no function prototype as in the first code snippet then the compiler performs default argument promotions on each argument that includes the integer promotions and conversion of expressions of type float to type double. If after these operations the number and types of promoted arguments do not correspond to the number and types of parameters the behaviour is undefined. The compiler can be unable to issue an error because the function definition can be in some other compilation unit.
here are relevant quotes from the C Standard (6.5.2.2 Function calls)
2 If the expression that denotes the called function has a type that
includes a prototype, the number of arguments shall agree with the
number of parameters. Each argument shall have a type such that its
value may be assigned to an object with the unqualified version of the
type of its corresponding parameter.
6 If the expression that denotes the called function has a type that
does not include a prototype, the integer promotions are performed on
each argument, and arguments that have type float are promoted to
double. These are called the default argument promotions. If the
number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that
includes a prototype, and either the prototype ends with an ellipsis
(, ...) or the types of the arguments after promotion are not
compatible with the types of the parameters, the behavior is
undefined. If the function is defined with a type that does not
include a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after promotion, the
behavior is undefined, except for the following cases:
— one promoted type is a signed integer type, the other promoted type
is the corresponding unsigned integer type, and the value is
representable in both types;
— both types are pointers to qualified or unqualified versions of a
character type or void.
As for your code snippets then if the second parameter had type double then the code would be well-formed. However as the second parameter has type float but the corresponding argument will be promoted to type double then the first code snippet has undefined behaviour.
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.
In the first case, main() performs integer promotions on each argument and float-to-double promotions. These are called "default argument promotions". So you'll probably end up calling the functions incorrectly, by passing an int and a double wheras the functions expect an int and a float.
See Default argument promotions in C function calls and the answers for more details.
With curiosity of the definition and scope of typedef I have written below C code in 2 .c files:
main.c
#include <stdio.h>
int main()
{
int a = 5, b = 6;
printf("a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
}
swap.c
typedef T;
void swap(T* a, T* b)
{
T t = *a;
*a = *b;
*b = t;
}
To my surprise, the code files could be compiled with Visual Studio C compiler (cl.exe /Tc main.c swap.c)
And the program runs correctly! From my understanding, typedef requires 2 parameters, but why this code compiles and runs?
For further analysis, in the main function, I declared another 2 float variables and try also to swap both after swapping the 2 integers, but this time it fails to compile (with cl.exe). What amazing is the code could be compiled and run with Tiny C (tcc.exe main.c swap.c), so it works like a template method!
Typedef is actually a declaration (it creates aliases for existing types), and is in no way limited to two 'parameters'. See Typedef Declarations (C) and Fundamental typedef operand syntax.
If you write typedef T;, you are declaring T to be an unspecified type (called "no specified type" in the C89 spec). It's a little (and only a very little, but conceptually this might help you) like #define X defines X but the preprocessor will replace it with the empty string (i.e. remove X).
So, you are typedefing T to be unspecified, which makes the arguments to your swap function of unspecified type.
What you are seeing here is that in C89 (but not C99, where it results in undefined behaviour - contrast ANSI 3.5.2 with ISOC99 6.7.2), unspecified types default to int, which is why your method works for integers but not floats in Visual Studio (presumably it disallows implicit integer typing by default). GCC will compile it with floats, though, provided you aren't using -Werror, which you probably should be.
I strongly suggest turning on some warnings: -Wall in gcc will spit out the following, among other things
swap.c:1:9: warning: type defaults to ‘int’ in declaration of ‘T’ [-Wimplicit-int]
The reason it "works" on floats is because floats and ints are probably the same size (32 bits) on your machine. Try it with double. Or char. Or short.
swap is really like this:
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
with you calling
swap((int*)&a, (int*)&b);
Try it and compare the results for yourself.
Edit: I just tried it in tcc. -Wall does not alert you to the implicit int typing, sadly.
In C90 (which is what MSVC follows as a baseline when compiling C code), one of the possible type specifiers is (C90 6.5.2 "Type specifiers" - emphasis added):
int, signed, signed int, or no type specifiers
so if no type specifier is provided in a declaration (including a typedef) then the type defaults to int. this is commonly known as an "implicit int" declaration. Note that C99 removed support for implicit int (by default GCC only issues a warning for this when compiling in C99 mode).
Your typedef:
typedef T;
is equivalent to:
typedef int T;
So your swap() defintion is equivalent to:
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
As it happens, when calling a function that hasn't been declared or prototyped (as occurs when you call swap() in main.c), the compiler will apply default argument promotions to arithmetic arguments and assume the function returns an int. Your call to swap() passes two arguments of type int*, so no promotion occurs (they're pointer arguments, not arithmetic). That happens to be exactly what the definition for swap() expects, so the function call works (and is well defined behavior).
Now, the calling code expects swap() to return an int since no declaration was seen, and your swap() function doesn't return anything (void). That is undefined behavior, but in this case there's no apparent problem (though it's still a bug in your code). However, if you change the definition of swap() so that it returns int:
int swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
the undefined behavior goes away, even though swap() doesn't seem to return anything. Since nothing is done with the result at the call site, C90 permits the function to return with an expression. C90 allows this in order to support pre-standard code where there was no such thing as a void type.
I thought the difference is that declaration doesn't have parameter types...
Why does this work:
int fuc();
int fuc(int i) {
printf("%d", i);
return 0;
}
but this fails compiling:
int fuc();
int fuc(float f) {
printf("%f", f);
return 0;
}
with the message:
error: conflicting types for ‘fuc’. note: an argument type that has a default promotion can’t match an empty parameter name list declaration
A declaration:
int f();
...tells the compiler that some identifier (f, in this case) names a function, and tells it the return type of the function -- but does not specify the number or type(s) of parameter(s) that function is intended to receive.
A prototype:
int f(int, char);
...is otherwise similar, but also specifies the number/type of parameter(s) the function is intended to receive. If it takes no parameter, you use something like int f(void) to specify that (since leaving the parentheses empty is a declaration). A new-style function definition:
int f(int a, char b) {
// do stuff here...
}
...also acts as a prototype.
Without a prototype in scope, the compiler applies default promotions to arguments before calling the function. This means that any char or short is promoted to int, and any float is promoted to double. Therefore, if you declare (rather than prototype) a function, you do not want to specify any char, short or float parameter -- calling such a thing would/will give undefined behavior. With default flags, the compiler may well reject the code, since there's basically no way to use it correctly. You might be able to find some set of compiler flags that would get it to accept the code but it would be pretty pointless, since you can't use it anyway...
prototype = forward declaration, so you can use it before you tell the compiler what it does. It still has parameters, however.
Useful in a lot of respects!
The declaration int fuc(float); tells the compiler that there exists a function fuc which takes a float and returns an int.
The definition int fuc(float f) { /*...*/ } tells the compiler what fuc actually is and also provides the declaration as well.
The difference between a declaration and definition is the difference between saying that a size 6 blue hat exists and and handing someone a size 6 blue hat: the declaration says that there is such a thing, the definition says that this thing right here is the thing in question.