Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default argument promotions in C99 standard

I have a question about default argument promotions in C99 standard. In the book "C Programming - A Modern Approach, 2nd Edition" I've read that:

Argument Conversions:

[...]

1) The compiler has encountered a prototype prior to the call. [...]

2) The compiler has not encountered a prototype prior to the call. The compiler performs the default argument promotions: (1)floatarguments are converted todouble. (2) The integral promotions are performed, causingcharandshortarguments to be converted toint. (In C99, the integer promotions are performed.)

A few lines further is shown an example in which there is no function prototype or definition before calling it. It is commented as follows:

Of course, a much better solution is to provide a prototype forsquarebefore calling it. In C99, callingsquarewithout first providing a declaration or definition of the function is an error.

Aren't those two cursive sentences kind of contrary with each other? I mean, if C99 forbids calling functions without previous declaration/definition how can it determine promotions in that kind of function call?

like image 423
Quentin Avatar asked Dec 26 '22 19:12

Quentin


2 Answers

No they are not contradictory.

A declaration is not necessarily a prototype:

int f();

declares the function f but isn't a prototype since nothing is known about the argument types.

int (a)
 in a;
{
 ...
}

is a definition but isn't a prototype either.

like image 149
Jens Gustedt Avatar answered Dec 29 '22 09:12

Jens Gustedt


C99 does not forbid you to call functions that have no prototype.

I can't give the details because you have not posted code for square() or the call to it, but presumably what is happening is that the promotion made to the arguments in the call to square() result in different types being passed to the function than it actually declares in the implementation.

That's what would result in undefined behavior, and would be an error.

For example:

// in foo.c:

int foo(void)
{ 
    char x = 42;

    return square(x); // x is promoted to int, but is wrong
}


// in bar.c

#include <stdio.h>
void bar(int x)
{ 
    square( &x); // this call is fine -  what is being passed is what square() expects
    printf("The result is %d\n", x);
}


// in square.c

void square( int* p)
{
    *p *= *p;
    return;
}

And if square() is defined with a prototype that declares the parameter to have an argument of type char, there's no way to call correctly it without a prototype having been 'seen' by the calling code since in such cases the argument will be promoted to int.

like image 44
Michael Burr Avatar answered Dec 29 '22 08:12

Michael Burr