Disassembly of a `int fn(); fn();` vs `int fn(void); fn();` [duplicate] - c

There is a curious difference between assemblies of a small program, when compiled as a C-program or as a C++-program (for Linux x86-64).
The code in question:
int fun();
int main(){
return fun();
}
Compiling it as a C-program (with gcc -O2) yields:
main:
xorl %eax, %eax
jmp fun
But compiling it as a C++-program (with g++ -02) yields:
main:
jmp _Z3funv
I find it puzzling, that the C-version initializes the return value of the main-function with 0 (xorl %eax, %eax).
Which feature of the C-language is responsible for this necessity?
Edit: It is true that, for int fun(void); the is no initialization of the eax-register.
If there is no prototype of fun at all, i.e.:
int main(){
return fun();
}
then the C-compiler zeros the eax-register once again.

In C int fun(); can take any number of arguments, so it may even be a varargs function. In C++ however it means it takes no arguments.
The x86-64 sysv abi convention demands that the register AL must contain the number of SSE registers used when invoking a varargs function. You of course pass no argument, so it is zeroed. For convenience the compiler decided to zero the whole eax. Declare your prototype as int fun(void); and the xor shall disappear.

Apparently it is a defensive measure, designed for situations when prototype-less fun function happens to actually be a variadic function, as explained by #Jester's answer.
Note though that this explanation does not hold any water from the point of view of standard C language.
Since the beginning of standardized times (C89/90) C language explicitly required all variadic functions to be declared with prototype before the point of the call. Calling a non-prototyped variadic function triggers undefined behavior in standard C. So, formally, compilers do not have to accommodate the possibility of fun being variadic - if it is, the behavior would be undefined anyway.
Moreover, as #John Bollinger noted in the comments, according to the C standard, a non-prototype int fun() declaration actually precludes further variadic prototype declarations of fun. I.e. a variadic function cannot be legally pre-declared as a () function. That would be another reason why the above non-prototype declaration is sufficient for the compiler to assume that fun cannot possibly be variadic.
This could actually be a legacy feature, designed to support pre-standard C code, where pre-declaring variadic functions with prototype was not required.

Related

Why does an invalid use of C function compile fine without any warnings?

I'm more like C++ than C, but this simple example of code is big surprise for me:
int foo() {
return 3;
}
int main() {
int x;
foo(&x);
return x;
}
https://godbolt.org/z/4ov9nTzjM
No warnings, no linking issues still this code is invalid.
I've stumbled on this when some beginner filed a question with code having this strange issue. Question was asked on some other site (none English one) and I'd like to provide him better explanation why it compiles (and learn something about C). His original code if some one needs to see it.
I'm suspecting that this sis somehow related to implicit function declaration, but I'm not fully sure. Why compiler doesn't complain that foo is called with arguments?
Update:
Here is an assembly:
foo:
push rbp
mov rbp, rsp
mov eax, 3
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
lea rax, [rbp-4]
mov rdi, rax
mov eax, 0
call foo
mov eax, DWORD PTR [rbp-4]
leave
ret
as you can see x remains uninitialized.
The compiler should issue a message for this program
int foo() {
return 3;
}
int main() {
int x;
foo(&x);
return x;
}
because the function foo called with an argument though its identifier list is empty. So the program has undefined behavior.
According to the C Standard *6.7.6.3 Function declarators (including prototypes)
)
14 An identifier list declares only the identifiers of the parameters
of the function. An empty list in a function declarator that is part
of a definition of that function specifies that the function has no
parameters. The empty list in a function declarator that is not part
of a definition of that function specifies that no information about
the number or types of the parameters is supplied.
So the program is invalid.
You could make it a valid program the following way
int foo();
int main() {
int x;
foo(&x);
return x;
}
int foo( int *p ) {
return 3;
}
Though the compiler can issue a warning that the parameter p is not used.
In this case the function declaration that is not its definition means that there is no information about the number and types of parameters.
Opposite to C in C++ this declaration
int foo();
is equivalent to
int foo( void );
This function is 100% correct.
In the C language int foo() { means: define function foo returning int and taking unspecified number of parameters.
Your confusion comes from C++ plus where int foo(void) and int foo() mean exactly the same.
In the C language to define function which does not take any parameters you need to define it as:
int foo(void) {
Empty parentheses are part of an old C grammar for function declarations that did not provide type checking. Prior to the creation of parameter lists with types, function definitions had a form like this:
int foo(a, b, c)
char a;
int *b;
float c;
{
// body of function
}
Then function declarations that were not definitions would just use (), as in int foo();. So a function definition would show what arguments a function expected, including the number of them and their types, but a declaration would not.
Of course, function definitions are often not visible when compiling a source file, as the definitions are in other files, although of course a declaration may be provided, often in header files. So, when a function is called, the compiler would not generally see the definition and would not generally know what arguments the function required. So it could not check the arguments. Thus, there is no requirement that the compiler provide a diagnostic message for a mismatch with functions declared with this old grammar.
The programmer is responsible for making the arguments match. The function definition does specify how many parameters the function has and does specify their types, and the C standard does not define the behavior if there is a mismatch. But that is a run-time issue; it is not something the compiler is required to diagnose, and it is not something the compiler can diagnose if the function definition is not visible to it.
When the definition is visible, the compiler could issue a diagnostic, especially if the definition appears prior to the call. Although GCC does not diagnose this, Clang does, if the definition appears first. So this is a missed opportunity for the GCC developers.
When the grammar for function prototypes (function declarations with types for the parameters) was created, () could not be used to indicate the function had no parameters, since it was already in use for the old grammar. So the notation (void) was invented for this; to declare, outside of a definition, that a function takes no parameters, you muse use (void), as in int foo(void);. This may also be used in a function definition, int foo(void) { … }.
When a function declaration has this new form, the compiler is required to check the number and types of arguments in a call to the function and to issue a diagnostic if there is a mismatch.
The function
int foo() {
return 3;
}
with an empty parameter list means in C a function with an undefined list of parameters. It's a legacy syntax from the times of pre-ANSI. Today it is considered an incomplete function definition (as you can later provide a more precise definition).
The compiler gives no warning because this construction was the only available when there were no function prototypes to define the interface of a function and the compiler didn't check the parameters passed to a function.
It is old, legacy use of C syntax, like the following (actually still accepted today, and surprises many language experts when they discover it is still accepted):
/* no function return type, defaults to int */
main(argc, argv)
/* no definition for argc, defaults to int */
char *argv[]; /* parameters are defined between the parameter list
* and the function body block */
{
/* ... */
}
which was a reminiscence of fortran.
The correct way of defining a function with no parameters is:
int foo(void)
{
/* ... */
}
which makes the compiler to emit an error (and not a warning) if you try to call it with parameters.

Strange integers in c language

I have code:
#include <stdio.h>
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
I know that I have to declare functions first, and only after that I can call them, but I want to understand what happends.
(Compiled by gcc v6.3.0)
I ignored implicit declaration of function warning and ran program several times, output was this:
1839551928
-2135227064
41523672
// And more strange numbers
I have 2 questions:
1) What do these numbers mean?
2) How function main knows how to call function sum without it declaration?
I'll assume that the code in your question is the code you're actually compiling and running:
int main() {
int a = sum(1, 3);
return 0;
}
int sum(int a, int b, int c) {
printf("%d\n", c);
return a + b + c;
}
The call to printf is invalid, since you don't have the required #include <stdio.h>. But that's not what you're asking about, so we'll ignore it. The question was edited to add the include directive.
In standard C, since the 1999 standard, calling a function (sum in this case) with no visible declaration is a constraint violation. That means that a diagnostic is required (but a conforming compiler can still successfully compile the program if it chooses to). Along with syntax errors, constraint violations are the closest C comes to saying that something is illegal. (Except for #error directives, which must cause a translation unit to be rejected.)
Prior to C99, C had an "implicit int" rule, which meant that if you call a function with no visible declaration an implicit declaration would be created. That declaration would be for a function with a return type of int, and with parameters of the (promoted) types of the arguments you passed. Your call sum(1, 3) would create an implicit declaration int sum(int, int), and generate a call as if the function were defined that way.
Since it isn't defined that way, the behavior is undefined. (Most likely the value of one of the parameters, perhaps the third, will be taken from some arbitrary register or memory location, but the standard says nothing about what the call will actually do.)
C99 (the 1999 edition of the ISO C standard) dropped the implicit int rule. If you compile your code with a conforming C99 or later compiler, the compiler is required to diagnose an error for the sum(1, 3) call. Many compilers, for backward compatibility with old code, will print a non-fatal warning and generate code that assumes the definition matches the implicit declaration. And many compilers are non-conforming by default, and might not even issue a warning. (BTW, if your compiler did print an error or warning message, it is tremendously helpful if you include it in your question.)
Your program is buggy. A conforming C compiler must at least warn you about it, and possibly reject it. If you run it in spite of the warning, the behavior is undefined.
This is undefined behavior per 6.5.2.2 Function calls, paragraph 9 of the C standard:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
Functions without prototypes are allowed under 6.5.2.2 Function calls, paragraph 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. ...
Note again: if the parameters passed don't match the arguments expected, the behavior is undefined.
In strictly standard conforming C, if you don't declare a function before using it, it will assume certain default argument types for the function.This is based on early versions of C with a weaker type system, and retained only for backwards compatibility. It should not be used generally.
Ill skip the details here, but in your case it assumes sum takes 2 ints and returns an int.
Calling a function with the wrong number of parameters, as you are doing here, is undefined behaviour. When you call sum, the compiler thinks that it takes two integers, so it passes two integers to it. When the function is actually called, however, it tries to read one more integer, c. Since you only passed 2 ints, the space for c contains random crap, which is what you're seeing when you print out. Note that it doesn't have to do this, since this is undefined behaviour, it could do anything. It could have given values for b & c, for example.
Obviously this behaviour is confusing, and you should not rely on undefined behaviour, so you'd be better off compiling with stricter compiler settings so this program wouldn't compile. (The proper version would declare sum above main.)
1) Since you haven't provided value for parameter "c" when calling function "sum" its value inside the function is undefined. If you declared function before main, your program wouldn't even compile, and you would get "error: too few arguments to function call" error.
2) Normally, it doesn't. Function has to be declared before the call so the compiler can check function signature. Compiler optimizations solved this for you in this case.
I'm not 100% sure if C works exactly like this but, your function calls work like a stack in memory. When you call a function your arguments are put on that stack so when in the fuction you can access them by selecting less x positions on memory. So:
You call summ(1, 3)
the stack will have 1 and the on the top a 3.
when executing the fuction it will see the last position of memory for the 1º argument (it recovers the 1) and then the position before that for the 2º argument (recovering the 3), however, there is a 3º argument so it accesses the position before that as well.
This position is garbige as not put by you and different everytime you run it.
Hope it was clear enought. Remeber that the stack works is inverted so every time you add something it goes to the previous memory position, not the next.

What are the semantics of function pointers with empty parentheses in each C standard?

Answers to this and this question say that function pointers of the form return-type (*pointer)() are pointers to a function which takes any number of arguments, though the latter says they obsolesced in C11.
On an i386 system with GCC, “extra” arguments passed in a call to an empty-parentheses-type’d function pointer are ignored, because of how stack frames work; e.g.,
/* test.c */
#include <stdio.h>
int foo(int arg) { return arg; }
int main(void)
{
int (*fp)() = foo;
printf("%d\n", fp(267239151, 42, (struct { int x, y, z; }){ 1, 2, 3 }));
return 0;
}
$ gcc -o test test.c && ./test
267239151
$
In which C standards are empty-parentheses’d function pointers allowed? and wherever so, what are they specified to mean?
N1570 6.11.6:
The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.
This same wording appears in the 1990, 1999, and 2011 editions of the ISO C standard. There has been no change. The word obsolescent says that the feature may be removed in a future edition of the Standard, but so far the committee has not done so. (Function pointer declarations are just one of several contexts where function declarators can appear.)
The Introduction section of the C standard explains what obsolescent means:
Certain features are obsolescent, which means that they may be
considered for withdrawal in future revisions of this International
Standard. They are retained because of their widespread use, but their
use in new implementations (for implementation features) or new
programs (for language [6.11] or library features [7.31]) is
discouraged.
A call to a function declared with an old-style declarator is still required to pass the correct number and type(s) of arguments (after promotion) as defined by the function's actual definition. A call with incorrect arguments has undefined behavior, which means that the compiler is not required to diagnose the error; the burden is entirely on the programmer.
This is why prototypes were introduced, so that the compiler could check correctness of arguments.
On an i386 system with GCC, “extra” arguments passed in a call to an
empty-parentheses-type’d function pointer are ignored, because of how
stack frames work ...
Yes, that's well within the bounds of undefined behavior. The worst symptom of undefined behavior is having the program work exactly as you expect it to. It means that you have a bug that hasn't exhibited itself yet, and it will be difficult to track it down.
You should not depend on that unless you have a very good reason to do so.
If you change
int (*fp)() = foo;
to
int (*fp)(int) = foo;
the compiler will diagnose the incorrect call.
Any function declarator can have empty parentheses (unless it's a function declaration where there is already a non-void prototype in scope). This isn't deprecated, although it is "obsolescent".
In a function pointer, it means the pointer can point to a function with any argument list.
Note that when actually calling a function through the pointer, the arguments must be of correct type and number according to the function definition, otherwise the behaviour is undefined.
Although C allows you to declare a function (or pointer to function) with an empty parameter list, that does not change the fact that the function must be defined with a precise of parameters, each with a precise type. [Note 1]
If the parameter declaration is not visible at a call site, the compiler will obviously not be able to perform appropriate conversions to the provided arguments. It is, therefore, the programmer's responsibility to ensure that there are a correct number of arguments, all of them with the correct type. For some parameter types, this will not be possible because the compiler will apply the default argument promotions. [Note 2]
Calling a function with an incorrect number of arguments or with an argument whose type is not compatible with the corresponding parameter type is Undefined Behaviour.
The fact that the visible declaration has an empty parameter list does not change the way the function is called. It just puts more burden on the programmer to ensure that the call is well-defined.
This is equally true of pointer to function declarations.
In short, the sample code in the question is Undefined Behaviour. It happens to "work" on certain platforms, but it is neither portable nor is it guaranteed to keep working if you recompile. So the only possible advice is "Don't do that."
If you want to create a function which can accept extra arguments, use a varargs declaration. (See open for an example.) But be aware of the limitations: the called function must have some way of knowing the precise number and types of the provided arguments.
Notes
With the the exception of varargs functions, whose prototypes end with .... But a declaration with an empty parameter list cannot be used to call a varargs function.
Integer types narrower than int are converted to int and float values to double.

Can Function declaration or function prototype affects the working of the program

I am new beginner in the programming world and while I was coding on Functions in C, I came to know that there are three most important parts of the functions namely-
Function declaration or function prototype
Function call
Function definition
But, while I was solving a problem on factorials of a number I came to know in my code that when I am not including function declaration or function protocol in my code above main() then still the code is running and giving the output as 120. So my question is why a function declaration or function prototype is not affecting the output of my program or is it not necessary to include function declaration or prototype above main().
-------Code-----
#include<stdio.h>
#include<conio.h>
// int fact(int);
main()
{
int x;
x=fact(5);
printf("The factorial is =%d",x);
}
int fact(int n)
{
int f=1,i;
for(I=1;i<=n;i++)
f=f*I;
return (f);
}
If you use a function without declaring it, the compiler may provide a default declaration. This is not part of modern C, but compilers may use old standards or be unduly lax in this regard. (It is better to require explicit declarations because it helps reduce mistakes.)
Any declaration of a function must match the definition of the function. The declaration gives the compiler information about how to call the function. If the declaration does not match, things can go wrong. So, yes, the declaration can affect your program; it can break it.
In this case, you got lucky, the default declaration of a function happened to match your function fact. The default declaration is a function that returns int and accepts whatever arguments you pass it, subject to default argument promotions. (Roughly speaking, the default argument promotions are that integers narrower than an int are converted to int, and float values are converted to double. So, if a function returns int and accepts only int and double arguments, it can match the default declaration.)
A good compiler will warn you that a declaration is missing or that a declaration is wrong. Your compiler has “switches” or other options that tell it what sorts of warning and error messages you want. You should ask for lots of warning messages.
Your compiler probably also has switches that say which version of C to use. (There have been several standard versions, most recently 1999 and 2011, and there are some non-standard variants.) You might want to request that your compiler use the C 2011 standard, until you have good reason to change that. (Unfortunately, if you are using Microsoft Visual C, I do not believe it supports C 2011.)

C function definition and declaration

I'm a Java programmer,I learnt a little C++ and now I'm studying a little C for my job.
I can't understand C behaviour about function declaration/definition and related function calls.
From K&R I know that in C (very different from C++) I can call a function that has not been previously declared,and the compiler assumes an implicit declaration of the type:
int main()
{
function(10); // implicit function declaration ( int function() )
}
and I know that such a declaration implies a function that accepts a fixed but indefinite number of arguments of any type (as long as each call is consistent with the others). And I know this is K&R C, before C89, but I want to know how it works as well.
Now, I have this test code, I can't understand:
#include <stdio.h>
function();
int main()
{
printf("hello %d",function(1,2,3));
implicit(11,12,32); // implicit function declaration ( implicit() )
}
int implicit(int b)
{
}
function(int a)
{
}
in the case of function the declaration (return type is assumed to be int,no assumption about the arguments) does match the definition (the compiler issues a warning) but if I call the function with the wrong arguments,it compiles!
Same for the function implicit.
I can't understand..
The thing you have to remember is that the compile is pretty much sequential when it comes to declarations and definition. When the compiler processes the call for function all it has is the declaration that, as you say, doesn't have any assumptions about the arguments, which means you can call the function with any argument you like. When the compiler sees the definition, it doesn't go back to issue an error about the call, it might issue a warning though.
As for the implicit function, when the compiler first sees it it will assume that the arguments are what you pass in the call when it deduces the declaration. Again it will not know anything else until it later sees the declaration, and may issue a warning then.
Calling a function with to many, or to few, arguments leads to undefined behavior, which is why implicitly declared functions are so dangerous, as well as using an empty argument list when declaring functions.
There is really nothing to understand. This is a legacy C behaviour, which was an extremely lax language. As long as compiler could generate assembly instruction, it would gladly compile your code, and leave it to you to clean up the mess.
This is why it compiles in your case. Compiler can generate instruction to call the function - so it does as asked.

Resources