Function declaration vs. definition C - c

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.

Related

Why don't I get a "conflicting types" error when function has no arguments?

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);
^

Default argument and parameter promotions in C

I was studying about default argument promotions and got stuck at one point. In C 2011 (ISO/IEC 9899:2011), the relevant part seem to be:
§6.5.2.2 Function calls
¶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.
In the last three lines of paragraph it talks about the function type that does not include a prototype while defining it.
It says if the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined.
Now i have a very silly doubt that if both the function declaration and function definition does not include a prototype as mentioned in this paragraph, so about which parameters they are talking about in last three lines of paragraph. And what is the meaning of "parameters after promotion" here as i have only studied about argument promotions. What is "parameter promotions"?
Also can you give example of the exceptional cases mentioned in the last. If someone can explain this with a proper example that would be really appreciable.
Before C was standardized (aka before C89), functions were defined differently. The style is still supported in C11 for backwards-compatibility. Don't use it unless the whole purpose is to have fun:
int add_ints(); //forward-declaration has no parameters
add_ints(a, b)
//implicit type for return and parameters is int, this only works in pre-standard C or C89/C90
//int a, b; //remove this comment in C99/C11 for it to compile (also add return type int)
{
return a + b; //side note: old K&R compilers required parantheses around the return expression
}
In a way, these functions have parameters that behave like varargs. The caller doesn't know what parameters the function expects (same as with varargs). It is able to pass it any parameters and any number of them. However, it is of course undefined behavior if the number of parameters in the call statement doesn't match the number of parameters in the declaration.
Of course, there is a problem that arises from this. If the caller wants to pass a short, how will it know whether the function is expecting a short (and pass it directly) or an int (and needs to convert it)? It cannot, so a common ground was reached. It has been decided that:
char and short get promoted to int
float gets promoted to double
This happens for all functions defined this way (K&R style) and for varargs parameters. This way, a K&R function will never expect a short parameter, thus the compiler will always promote short parameters to int.
Of course, as #aschepler said, you can still define the function like:
short add_shorts(a, b)
short a, b;
{
return a + b;
}
This means that the parameters are first converted to int and passed to the function and only then does the function convert them to short and add them.
Be careful with functions like printf():
printf("%.f", 3); //passes an int: UB and also wrong answer (my compiler prints 0)
printf("%.f", 3.0); //correct
printf("%.f", (double)3); //correct
You may actually see K&R functions quite often, especially if the author didn't pay attention to add the void keyword to a function that takes no parameters:
int f1() //K&R function
{
return 0;
}
int f2(void) //Standard function
{
return 0;
}
int main(void) //Don't forget void here as well :P
{
int a = f1(); //Returns 0
int b = f2(); //Returns 0
int c = f1(100); //UB - invalid number of parameters, in practice just returns 0 :)
int d = f2(100); //Compiler error - parameter number/types don't match
//A good compiler would give a warning for call #3, but mine doesn't :(
}
EDIT: Not sure why, but cppreference classifies functions defined like f1() as their own type of function (parameter-less without void), instead of K&R functions. I don't have the standard in front of me, but even if the standard says the same thing, they should behave the same and they have the history I mentioned.
Default argument promotions
Function declarations in C

Float function always returning 0.000000

I'm a beginner with C and I can't find the solution for this "error" ...
(I don't want to just print what I read with scanf, that's just an extract from a bigger program, so can you try not to create a new code but to modify this ?)
Here's the code
#include <stdio.h>
float function(x){
return x;
}
int main(){
float x;
scanf("%f", &x);
printf("%f \n", function(x));
return 0;
}
And if you don't mind ... can you explain the reason why it doesn't work in this way ? Thank you a lot
You defined your function as:
float function(x){
return x;
}
If you want the function to take a float argument, you need to say so:
float function(float x){
return x;
}
Fixing that should fix your problem. You don't really need to worry about why your incorrect program behaves the way it does.
But if you're curious ...
Prior to the C99 standard, C had an "implicit int" rule. In this case, the parameter x would be of type int because you didn't specify its type explicitly. But since you used an old-style definition, not a prototype, you haven't told the compiler that the argument should be of type int. (Yes, old-style function declarations and definitions can cause some serious problems. That's why we have prototypes.)
So when you call your function with a float argument, the compiler doesn't complain, and it doesn't convert your argument from float to int. Instead, it promotes your argument from float to double (that's just one of the rules for calling a function with no visible prototype, or for calling a variadic function like printf), and the function itself assumes that you really passed it an int.
Result: The behavior is undefined.
One likely result is that the function will treat the double argument as if it were of type int, yielding garbage. Another is that it will read the int value from some other memory location or register, yielding even worse garbage.
Always enable warnings. Always pay attention to any warnings your compiler produces. Always ask your compiler to conform to the C99 or C11 standard unless you have a specific reason not to. And always declare and define your functions with prototypes. (A prototype is a function declaration that specifies the types of the parameters.)
This also means that int main() should be int main(void). That's not likely to matter (unless you call main recursively, which would be silly), but it's a good habit.
You never defined the type of the function's parameter x. Without an explicit type, the type defaults to int. As a result, the float that you pass to the function gets converted to an int and gets truncated.
Add the parameter type to the function definition.
float function(float x){
return x;
}

Why does an empty declaration work for definitions with int arguments but not for float arguments?

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.

C: type conversion when passing an argument on a function call

From The C Programming Language 2nd Edition:
Since an argument of a function call is an expression, type conversions also take place when arguments are passed to function. In absence of a function prototype, char and short become int, and float becomes double.
By reading the text, I am getting an impression that unless you explicitly specify the argument type by either using cast or function prototype, function arguments will always be passed as either passed as int or double.
In order to verify my assumption, I compiled the following code:
#include <stdio.h>
main()
{
unsigned char c = 'Z';
float number = 3.14f;
function_call(c, number);
}
void function_call(char c, float f)
{
}
After compilation I get the following warnings:
typeconversion.c:11: warning: conflicting types for ‘function_call’
typeconversion.c:7: warning: previous implicit declaration of ‘function_call’ was here
My guess is c and number were both converted to int and double on the function call, and were then converted back to char and float. Is this what actually happened?
Casts are irrelevant, it's the (possibly implicit) prototype that matters.
void foo(short s) {
// do something
}
int main(void) {
signed char c = 'a';
foo(c); // c is promoted to short by explicit prototype
bar(c); // c is promoted to int by implicit prototype
}
void bar(int i) {
// do something
}
When the book says "an argument of a function call is an expression" it means that the same type promotion rules apply. It might be easier to understand if you think of a function argument as an implicit assignment to the variable specified in the function prototype. e.g. in the call to foo() above there's an implicit short s = c.
This is why casts don't matter. Consider the following code snippet:
signed char c = 'a';
int i = (short) c;
Here the value of c is promoted first to short (explicitly) then to int (implicitly). The value of i will always be an int.
As for char and short becoming int and float becoming double that refers to the default types for implicit function prototypes. When the compiler sees a call to a function before it has seen either a prototype or the definition of the function it generates a prototype automatically. It defaults to int for integer values and double for floating-point values.
If the eventual function declaration doesn't match to implicit prototype, you'll get warnings.
You've got the general idea of what's wrong, but not exactly.
What happened is that when you wrote
function_call(c, number);
The compiler saw that you were calling a function that it had not seen yet, and so had to decide what its signature should be. Based on the promotion rule you quoted earlier, it promoted char to int and float to double and decided that the signature is
function_call(int, double)
Then when it sees
function_call(char c, float f)
it interprets this as a signature for a different function with the same name, which is not allowed in C. It's exactly the same error as if you prototype a function differently from how you actually define it, just that in this case the prototype is implicitly generated by the compiler.
So, it is this rule that's causing the issue, but the error doesn't have anything to do with actually converting values back and forth between types.
The compiler is complaining that it assumed function_call is an int returning function as indicated by the standard, and then you tell it is a void function. The compiler won't care about arguments unless you are explicitely declaring them to be different to the actual function. You can pass it no arguments and it won't complain.
You must always declare your functions because this error will go undetected if the function is in other modules. If the function should return any type that could be larger than int such as void* or long, the cast to int in the caller function will most likely truncate it, leaving you with a weird bug.
Everyone has missed one thing. In ISO C an ISO-syntax prototype overrides default argument promotion.
And in that case the compiler is allowed to generate different code (!) based purely on the style of definition. This gets you K&R compatibility, but you can't always call between the language levels unless you have written the ISO code as K&R expects or modified the K&R code to see the ISO prototypes.
Try it with cc -S -O ...
extern float q; void f(float a) { q = a; }
movl 8(%ebp), %eax
movl %eax, q
extern float q; void f(a) float a; { q = a; } // Not the same thing!
fldl 8(%ebp)
fstps q

Resources