I don't understand why the following code generates a warning:
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)() = func;
return 0;
}
I thought that in C, a function with an empty parameters list mean a function that can receive an unknown number of parameters. func4 happens to receive 3 parameters. So why is it incompatible with myPointer?
Its especially confusing because the following does compile without warning:
void boo()
{
return;
}
int main()
{
int(*pointerDude)(double) = boo;
return 0;
}
What's going on?
The difference between the two cases is explained like this: if you pass a parameter to a function that accepts none, you are just consuming some memory on the stack. If you don't pass any parameter to a function that accepts a few, the latter is going to read random data from the stack.
Update: changed to community wiki to add these corrections from Pascal Cuoq:
Casts of function pointer in both directions are legal, informative warnings from the compiler notwithstanding. Calling a function with the wrong prototype is illegal in both cases. Some ABIs mandate that the function clean up the stack, in which case passing parameters to functions that accept none corrupt the stack as surely as not passing parameters to functions that expect them.
C99 6.3.2.3 par. 8:
A pointer to a function of one type may be converted to a pointer to a
function of another type and back again; the result shall compare
equal to the original pointer. If a converted pointer is used to call
a function whose type is not compatible with the pointed-to type, the
behavior is undefined
This is the warning you get:
warning: initialization from incompatible pointer type
You are passing void which means that will require a cast. You should use the right types.
IIRC C compiler has certain assumptions which can cause very odd bugs because you're not telling it how much stack space it needs whenever that function is called. Especially since the definitions don't match this might allow someone to inadvertently read your function pointer declaration and assume no parameters and have the function return garbage. If you really want to take variable numbers of parameters use my_func(...) and varargs.
A warning is just that, a warning. Honest.
In that case, the compiler knows you have an incorrect function pointer type and it blatantly tells you that. If you try to pass a number of parameters different then 3, then it will be issued an error.
I think that the problem is related to the casting of the parameters, because char is not 32 bits. Note that if you change the char to int this works fine. It is related to automatic promotion of variables.
Compare the two:
int func();
int func(double a, int b, int c)
{
return 0;
}
and
int func();
int func(double a, int b, char c)
{
return 0;
}
gcc gives you warning for the second, but not for the first
In C an empty parameter list means that the function does not take any parameters. I believe you are confusing an empty parameter list with varargs.
Changing your first example to include the parameters will remove the warning.
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)(double, int, char) = func;
return 0;
}
Related
I'm studying C and my book is explaining how to "prototype a function" so we can use it before we define it.
The point is that I can't imagine a situation in which is needed to use a function before have defined it, why can't we just define it at the beginning?
Can you please provide an example in which is strictly necessary to prototype a function (if it exists)?
Consider the program below:
#include <stdio.h>
void add(int, int);
int main() {
add(5, 3);
return 0;
}
void add(int a, int b) {
printf("%d", a+b);
}
Here, when you call add(5, 3) from main(), the compiler knows what add() is - the parameters it takes and the return type, thanks to the statement void add(int, int) in line 3. If this statement is commented out, then the compiler won't know what add() is and would give you an error. Thus, line 3 tells the compiler that you have defined the function add() later, which makes it perfectly eligible to be used in main(). Alternately, if you write the add() function before main(), then the compiler knows what add() is when you call it; so a function prototype is not required in this case.
Hope this is useful.
One use-case is when you want to take a function pointer to a function defined in another source file. (Or hand-written in asm). Taking a function-pointer always requires the function to be declared ahead of time, either by a definition or a prototype.
C89 (and earlier) allows implicit declaration of functions (use them even if there's no definition or prototype earlier in the compilation unit (this .c + anything you #include).
So you can compile this in a file by itself, in C89 (or with a warning from most compilers in C99 / C11 mode)
int foo() {
return bar( 123 ); /* implicit declaration of int bar() */
}
But you can't use &bar as a function pointer
/* int bar(int); necessary if you haven't defined int bar(int x) { ... } */
void *take_fptr() {
return &bar; // error: 'bar' undeclared from GCC
}
You can also construct cases where a declaration is needed to get args passed correctly, e.g. if you want your function to take a float arg, you need it to be declared, either by a definition or prototype. The "Default Argument Promotions" in the ISO C standard will promote float to double when passing an arg to an unprototyped function, or to the ... part of a variadic function like printf.
(That's why %f prints a double, not a float, and why %lf is also a double if it's supported at all. Also why functions like putchar(int) take their char arg as an int: default promotions of narrow integer types up to int. Some parts of the C standard library date back to very early C, before the language supported prototypes!)
Or for example, if your function is expecting a long long, but the caller writes bar( 123 ), the caller will only infer int, which may be passed differently from a long long. But you can fix that by writing bar( 123LL ), or bar( (long long)123 )
Or if the return type isn't int (or void), then you also need the compiler to know about the function via a declaration. Implicitly-declared functions are assumed to return int. Depending on the calling convention and architecture, you might get away with that for a short return type, or on some machines even for a char*, e.g. on 32-bit machines where intptr_t is int. (Early C relied on that.)
But in general, e.g. on machines with 64-bit pointers, implicit-int return isn't going to work. Nor will it work for a double return value, or a struct.
The definition of the function may not be known at that time. Imagine a program that calls malloc to allocate memory. At compile time, nobody has any idea what allocator the program will be using at run time. So how can you define the function then?
The Problem
I'm writing the following code, which, as excerpted below, should return the total amount of money depending on the number of each type of coin.
static int qNum = 0;
static int dNum = 0;
static int nNum = 0;
static int pNum = 0;
int main(){
float totalCost;
totalCost = totalMoneyCalc();
}
float totalMoneyCalc(void){
float total;
total = qNum*.25 + dNum*.10 + nNum*.05 + pNum*.01;
return total;
}
The Errors I'm Getting
note: previous implicit declaration of ‘totalMoneyCalc’ was here
totalCost = totalMoneyCalc();
error: conflicting types for ‘totalMoneyCalc’
float totalMoneyCalc(){
However, when I remove the "float" part of the totalMoneyCalc() function, it doesn't return any more errors. Aren't you supposed to assign functions types?
What I've Tried
I looked at this question on SO, which says something about having to pass in void as a param for functions that don't take in anything but adding that to the original code didn't work either.
I also checked out some primers on using floats for functions in C, but I followed those and it's still not working.
Am I supposed to be doing something with pointers? I don't understand why returning a value in a function to be assigned to a variable is different from ints to floats. If anyone has suggestions, fixes, or explanations, I'd really appreciate it.
Yes, all C functions have types. Prior to the 1999 standard, it was legal to omit the return type from a declaration; that would implicitly give it a return type of int. As of C99, the return type must always be specified explicitly.
Also prior to C99, it was legal to call a function with no visible declaration. The compiler would assume a function with a return type of int (that's the "previous implicit declaration" referred to in the error message). C99 dropped that rule; now all functions must be visibly declared before you can call them.
Assuming you're using gcc (that looks like a gcc error message), the compiler enforces the C90 rules by default. The problem it's reporting is that the implicit declaration of totalMoneyCalc (as a function returning int) conflicts with the later explicit declaration as a function returning float.
The solution is to declare totalMoneyCalc before the call. You can either add a separate "forward" declaration before main:
float totalMoneyCalc(void);
or you can move the entire definition above main. (This is a good idea even in C90.)
A minor point: int main() should be int main(void).
The compiler needs to know about the function before you use it. If the function is unknown to the compiler then it will not be able to know whether the function returns double or float or if it's first parameter is a pointer or a struct, so it will by default think of them all as int's.
You can to add a function prototype before main()
float totalMoneyCalc(void);
or move the entire function definition before main().
Say I have this function:
int func2() {
printf("func2\n");
return 0;
}
Now I declare a pointer:
int (*fp)(double);
This should point to a function that takes a double argument and returns an int.
func2 does NOT have any argument, but still when I write:
fp = func2;
fp(2);
(with 2 being just an arbitrary number), func2` is invoked correctly.
Why is that? Is there no meaning to the number of parameters I declare for a function pointer?
Yes, there is a meaning. In C (but not in C++), a function declared with an empty set of parentheses means it takes an unspecified number of parameters. When you do this, you're preventing the compiler from checking the number and types of arguments; it's a holdover from before the C language was standardized by ANSI and ISO.
Failing to call a function with the proper number and types of arguments results in undefined behavior. If you instead explicitly declare your function to take zero parameters by using a parameter list of void, then the compiler will give you a warning when you assign a function pointer of the wrong type:
int func1(); // declare function taking unspecified parameters
int func2(void); // declare function taking zero parameters
...
// No warning, since parameters are potentially compatible; calling will lead
// to undefined behavior
int (*fp1)(double) = func1;
...
// warning: assignment from incompatible pointer type
int (*fp2)(double) = func2;
You need to explicitly declare the parameter, otherwise you'll get undefined behavior :)
int func2(double x)
{
printf("func2(%lf)\n", x);
return 0;
}
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.
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