Help on 6.5.2.2p6 (function calls)

F

Francis Moreau

Hello,

I think I've mostly understood this section but the last part is still
obscure for me.

Here is what it says:

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:

[...]

I fail to understand "parameters after promotion". I thought that
promotions were applied to arguments only (not parameters).

So here is a definition without prototype:

void foo(a, b, c) { return; }

I thought that type of parameters default to int, so there's no need
for promotions here.

Could anybody shed some light here ?

Thanks
 
D

Dann Corbit

Hello,

I think I've mostly understood this section but the last part is still
obscure for me.

Here is what it says:

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:

[...]

I fail to understand "parameters after promotion". I thought that
promotions were applied to arguments only (not parameters).

So here is a definition without prototype:

void foo(a, b, c) { return; }

I thought that type of parameters default to int, so there's no need
for promotions here.

Could anybody shed some light here ?

Consider:

#include <stdio.h>

float f = 3.14159;

printf("%g\n", f);

The type of f is float, but it will be promoted to double.

As an example, consider this question from the C-FAQ:

12.9: Someone told me it was wrong to use %lf with printf(). How can
printf() use %f for type double, if scanf() requires %lf?

A: It's true that printf's %f specifier works with both float and
double arguments. Due to the "default argument promotions"
(which apply in variable-length argument lists such as printf's,
whether or not prototypes are in scope), values of type float
are promoted to double, and printf() therefore sees only
doubles. (printf() does accept %Lf, for long double.)
See also questions 12.13 and 15.2.

References: K&R1 Sec. 7.3 pp. 145-47, Sec. 7.4 pp. 147-50; K&R2
Sec. 7.2 pp. 153-44, Sec. 7.4 pp. 157-59; ISO Sec. 7.9.6.1,
Sec. 7.9.6.2; H&S Sec. 15.8 pp. 357-64, Sec. 15.11 pp. 366-78;
CT&P Sec. A.1 pp. 121-33.

and similarly:

15.2: How can %f be used for both float and double arguments in
printf()? Aren't they different types?

A: In the variable-length part of a variable-length argument list,
the "default argument promotions" apply: types char and
short int are promoted to int, and float is promoted to double.
(These are the same promotions that apply to function calls
without a prototype in scope, also known as "old style" function
calls; see question 11.3.) Therefore, printf's %f format always
sees a double. (Similarly, %c always sees an int, as does %hd.)
See also questions 12.9 and 12.13.

References: ISO Sec. 6.3.2.2; H&S Sec. 6.3.5 p. 177, Sec. 9.4
pp. 272-3.

And even a bit more esoteric:

15.10: I have a varargs function which accepts a float parameter. Why
isn't

va_arg(argp, float)

working?

A: In the variable-length part of variable-length argument lists,
the old "default argument promotions" apply: arguments of type
float are always promoted (widened) to type double, and types
char and short int are promoted to int. Therefore, it is never
correct to invoke va_arg(argp, float); instead you should always
use va_arg(argp, double). Similarly, use va_arg(argp, int) to
retrieve arguments which were originally char, short, or int.
(For analogous reasons, the last "fixed" argument, as handed to
va_start(), should not be widenable, either.) See also
questions 11.3 and 15.2.

References: ISO Sec. 6.3.2.2; Rationale Sec. 4.8.1.2; H&S
Sec. 11.4 p. 297.
 
D

Dann Corbit

Hello,

I think I've mostly understood this section but the last part is still
obscure for me.

Here is what it says:

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:

[...]

I fail to understand "parameters after promotion". I thought that
promotions were applied to arguments only (not parameters).

So here is a definition without prototype:

void foo(a, b, c) { return; }

I thought that type of parameters default to int, so there's no need
for promotions here.

Could anybody shed some light here ?

Also pertinent:

11.3: My ANSI compiler complains about a mismatch when it sees

extern int func(float);

int func(x)
float x;
{ ...

A: You have mixed the new-style prototype declaration
"extern int func(float);" with the old-style definition
"int func(x) float x;". It is usually possible to mix the two
styles (see question 11.4), but not in this case.

Old C (and ANSI C, in the absence of prototypes, and in
variable-length argument lists; see question 15.2) "widens"
certain arguments when they are passed to functions. floats
are promoted to double, and characters and short integers are
promoted to int. (For old-style function definitions, the
values are automatically converted back to the corresponding
narrower types within the body of the called function, if they
are declared that way there.)

This problem can be fixed either by using new-style syntax
consistently in the definition:

int func(float x) { ... }

or by changing the new-style prototype declaration to match the
old-style definition:

extern int func(double);

(In this case, it would be clearest to change the old-style
definition to use double as well, if possible.)

It is arguably much safer to avoid "narrow" (char, short int,
and float) function arguments and return types altogether.

See also question 1.25.

References: K&R1 Sec. A7.1 p. 186; K&R2 Sec. A7.3.2 p. 202; ISO
Sec. 6.3.2.2, Sec. 6.5.4.3; Rationale Sec. 3.3.2.2,
Sec. 3.5.4.3; H&S Sec. 9.2 pp. 265-7, Sec. 9.4 pp. 272-3.
 
F

Francis Moreau

Hello,

I think I've mostly understood this section but the last part is still
obscure for me.

Here is what it says:

  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:

  [...]

I fail to understand "parameters after promotion". I thought that
promotions were applied to arguments only (not parameters).

So here is a definition without prototype:

   void foo(a, b, c) { return; }

I thought that type of parameters default to int, so there's no need
for promotions here.

Could anybody shed some light here ?

Well I think my misunderstanding is coming from the fact that I don't
know the "old style" function definition.

Can anybody gives me some pointers about it ?

Thanks
 
D

Dann Corbit

Hello,

I think I've mostly understood this section but the last part is still
obscure for me.

Here is what it says:

  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:

  [...]

I fail to understand "parameters after promotion". I thought that
promotions were applied to arguments only (not parameters).

So here is a definition without prototype:

   void foo(a, b, c) { return; }

I thought that type of parameters default to int, so there's no need
for promotions here.

Could anybody shed some light here ?

Well I think my misunderstanding is coming from the fact that I don't
know the "old style" function definition.

Can anybody gives me some pointers about it ?

If anyone is still using a C compiler that does not at least adhere to
the first ANSI/ISO standard, then time to upgrade.

Anyway, pre-ANSI C looked like this (example from ZLIB):

/*
====================================================================
*/
int ZEXPORT compress (dest, destLen, source, sourceLen)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
uLong sourceLen;
{
return compress2(dest, destLen, source, sourceLen,
Z_DEFAULT_COMPRESSION);
}

ZEXPORT is a macro that will vary by compiler used to make the symbol
public to end-users.

You will notice in the function declaration:
int ZEXPORT compress (dest, destLen, source, sourceLen)

there are no types to be seen. Those are published right below the
function declaration but before the function body.
In this case, we have a bunch of types that have been defined via
typedef.

Generally speaking, I take monstrosities such as this and feed them to
protoize so that I get something sensible to work with.
Unfortunately, protoize does not always work.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top