views:

1126

answers:

4

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?

+7  A: 

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.

Tyler McHenry
Adam Rosenfield
I don't think the question was about the error per se, although the two are certainly related.
Michael Carman
The only question mark I see is attached to a sentence asking if his interpretation of the error cause was correct. But yes, there are definitely several points to answer in this question.
Tyler McHenry
+4  A: 

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.

Michael Carman
your post answers many questions that I had prior to posting this question.
Midnight Blue
Glad to help, Midnight Blue.
Michael Carman
This answer mixes "prototype" with "declaration". A prototype is what declares the type of function parameters. A declaration is what may include a prototype and what the whole thing is called ("void f();"). So you want to say "implicit declaration" and "explicit declaration" instead.
Johannes Schaub - litb
+2  A: 

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.

jbcreix
The compiler error has nothing to do with the arguments he is passing. You assumed it had, you modded me down. The other answers disregard this and make the OP think that his compiler will catch wrong argument errors when it won't. Whatever.
jbcreix
+1  A: 

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
DigitalRoss
Well this is interesting...
Artelius