What default promotions of types are there in the variadic arguments list?
Look in the draft n1256 (C99 with Technical corrigenda TC1, TC2, and TC3 included) for 6.5.2.2 Function calls
:
For functions without prototype, or parameters corresponding to the ellipsis ...
, the default argument promotions are performed.
Those are: Default integer promotions and promotion of float
to double
.
Default integer promotions: Every integer type of rank less than int
is promoted to int
or unsigned int
.
Default argument promotion in a variadic function
None of the conditions in Paragraph 6 apply to your example. A function without a prototype refers to a function declared using the archaic K&R syntax:
int f();
When you call a function with this type of declaration, all arguments undergo default promotions.
Paragraph 6 also describes other situations where there's a prototype and the types in the call are not compatible to with the types in the prototype, but your types are compatible (they're the same).
Paragraph 7 says that argument conversion is performed for the first two arguments; they're converted to float
as specified in the prototype. Since a
and b
are already float
, no conversion is necessary.
The remaining arguments undergo default argument promotion (as described in Paragrah 6), since they correspond to the ellipsis in the prototype. The literals 4.0
and 5.0
have type double
, so no promotion is necessary.
better understanding type promotion of variadic parameters in c
"int
type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?" No. According to the Standard, int
s must be at least 16 bits in width (§5.2.4.2.1), but there is no further stipulation.
When you pass a long long int
to printf()
it is not subject to the integer promotions (§6.3.1.1 2):
The following may be used in an expression wherever an int or unsigned
int may be used:
- An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to
the rank of int and unsigned int.- A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions.58) All other types are unchanged by the integer
promotions.
If you pass a long double
to printf()
no conversion is made (§6.5.2.2 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.
The conversion specifiers corresponding to the arguments in the printf()
statement have no bearing on these promotions and conversions, except insofar as there will be undefined behavior if the specifiers and the types of their corresponding arguments do not match.
So, the integer promotions are performed, and float
s are converted to double
s, but "No other conversions are performed implicitly" (§6.5.2.2 8).
Addressing your edit to the question: "this makes me think that not all parameters are promoted to int
." Correct. Only integer types with integer conversion rank "less than or equal to the rank of int
and unsigned int
" are subject to integer promotion. It is simpler for floating point types; float
s are promoted to double
. That is all.
It may be worth pointing out that, according to §6.2.5 10, there are three real floating point types, float
, double
, and long double
. The values which may be held by a float
are a subset of the values which may be held by a double
, which are in turn a subset of the values which may be held by a long double
. Hence, there is no possibility of promotion for long double
types.
Further, according to §6.3.1.1 1:
The rank of long long int shall be greater than the rank of long int,
which shall be greater than the rank of int, which shall be greater
than the rank of short int, which shall be greater than the rank of
signed char.
So there is no possibility of a long long int
or long int
ever being promoted to int
or unsigned int
.
As for your final concern that promotion of a short
to an int
may, in some implementation, be impossible without losing some bits, note that §6.2.5 8 guarantees that an int
must be able to contain a short
, since the conversion rank of an int
must be greater than that of a short
:
For any two integer types with the same signedness and different
integer conversion rank (see 6.3.1.1), the range of values of the type
with smaller integer conversion rank is a subrange of the values of
the other type.
I want default argument promotion's example
For historical reasons, functions in C can be declared the old way, without a prototype, as:
type function()
or the new way, with a prototype, as:
type function(type parameter)
type function(type parameter, type parameter)
type function(type parameter, type parameter, type parameter)
… and so on
Thus, a function declaration in which the types of the parameters are given is said to have a prototype. This affects preparation of the arguments when calling a function. With a prototype, the arguments are converted to the declared types of the parameters. Without a prototype, the arguments are converted to default types, using rules called the default argument promotions. (Also, if a function is just being declared, not defined, the names of the parameters can be omitted, leaving a prototype that just lists types, as in type function(type, type, type)
.)
Because of the old grammar, to declare a function with a prototype that says it has no parameters, you need to explicitly say void
: type function(void)
. (This is different in C++, which does not have to support the old grammar. In C++
, type function()
is a prototype with no parameters.)
Additionally, a prototype can specify that there are variable arguments by putting ,...
after one or more regular parameters:
type function(type parameter,...)
In this case, the first arguments are converted to the parameter types, but the arguments that correspond to the ...
are converted using the default argument promotions. printf
is declared this way.
The default argument promotions are:
- Integers narrower (technically, of lesser rank) than
int
are promoted toint
if it can represent all the values of the source type orunsigned int
otherwise. float
arguments are converted todouble
.
There is also some finickiness about bit-fields in the default argument promotions, which I cannot say has ever arisen in code for me.
History
In the old C grammar, a function would be defined with:
type function(name0, name1, name2)
type name0;
type name1;
type name2;
and it would be declared without a prototype, as with type function()
. This means the caller did not know the actual types of the parameters. But you could not just pass a char
value for a char
argument or a short
value for a short
argument, because C, in trying to be flexible so it could work on many types of computers, had rules about char
and short
values being promoted to int
in expressions. Additionally, character constants like 'X'
have type int
, not char
, so, if somebody called a function with foo('X')
, the compiler would not know if foo
really wanted just a char
rather than an int
. So the default argument promotions were made to match the integer promotions.
Later versions of C fixed this by providing a way to declare the argument types in declarations visible to the caller, so the arguments always match the parameters (and the compiler has more information it can use to provide error messages). But the old grammar still has to be supported so that old code can be compiled.
More
The phrase in the C standard, “the expression that denotes the called function,” is used because you can call functions through pointers, not just their names. For example, we can write:
int (*FunctionPointer)() = (int (*)()) printf;
and then call printf
using FunctionPointer("Hello, world.\n");
. Then “the expression that denotes the called function” is FunctionPointer
, and it does not have a prototype even though printf
does. (There is no good reason to do this with the printf
function, but some esoteric code may do some unsavory things.)
You can initially declare a function without a prototype:
int foo();
and later add a prototype:
int foo(float x, char *y);
The compiler will merge the declarations, and the resulting foo
will have a prototype.
Default argument promotions in C function calls
Upvoted AProgrammer's answer—those are the real goods.
For those of you who are wondering why things are this way: in the dark ages before 1988, there was no such thing as a function prototype in classic "K&R" C, and the default argument promotions were instituted because (a) there were essentially "free", as it costs no more to put a byte in a register than to put a word in a register, and (b) to cut down on potential errors in parameter passing. That second reason never quite cut it, which was why the introduction of function prototypes in ANSI C was the single most important change ever in the C language.
As to when default promotions kick in: default argument promotions are used exactly when the expected type of the argument is unknown, which is to say when there's no prototype or when the argument is variadic.
Sized integers and promotions in varargs functions
To determine if some type
becomes int
, unsigned
or stays the same type when passed to a function with ...
, code could use _Generic()
. (C99 or later)
By using *1
, this mimics the usual promotions seen with f(format, ...)
.
Now your printf-like function can determine if the argument was promoted to int
, unsigned
or left as-is.
#include <stdio.h>
#define PromoType(v) _Generic(v*1, \
unsigned : "unsigned", \
int : "int", \
default: "no change" \
)
int main(void) {
int16_t i16;
puts(PromoType(i16));
uint16_t u16;
puts(PromoType(u16));
int32_t i32;
puts(PromoType(i32));
uint32_t u32;
puts(PromoType(u32));
int64_t i64;
puts(PromoType(i64));
uint64_t u64;
puts(PromoType(u64));
return 0;
}
Output
int
int
int
unsigned
no change
no change
Do enum types undergo default argument promotion?
Yes, default argument promotion can happen depending on the underlying type chosen for an enum
.
From section 6.7.2.2 of the C standard regarding enumeration specifiers:
Each enumerated type shall be compatible with
char
, a signed integer
type, or an unsigned integer type. The choice of type is
implementation-defined, but shall be capable of representing the
values of all the members of the enumeration. The enumerated type is
incomplete until immediately after the}
that terminates the list of
enumerator declarations, and complete thereafter
So an implementation may choose to use a type smaller than int
as the underlying type, and if so then it is subject to default argument promotions. GCC in particular will do this if you specify the -fshort-enums
flag.
And if that's the case, you can't use that enum
as the last named argument in a variadic function. From section 7.16.1.4p4 regarding va_start
:
The parameter
parmN
is the identifier of the rightmost parameter in
the variable parameter list in the function definition (the one just
before the ,...
). If the parameterparmN
is declared with theregister
storage class, with a function or array type, or with a type
that is not compatible with the type that results after application of
the default argument promotions, the behavior is undefined.
Related Topics
How to an Share Address Mapping Between Two Unrelated Processes on Linux
How to Avoid Multiple Definition Linking Error
Illegal Token on Right Side of ::
Linking Fortran and C++ Binaries Using Gcc
How to Get the Function Pointer of a Built-In Standard Operator
Purpose of Trigraph Sequences in C++
Trivial VS. Standard Layout VS. Pod
Effective C++ Item 23 Prefer Non-Member Non-Friend Functions to Member Functions
Why Can't I Add a Qgridlayout to a Qmainwindow
/Usr/Lib64/Libstdc++.So.6: Version 'Glibcxx_3.4.15' Not Found
Linux X11 - Global Keyboard Hook
When Are Static and Global Variables Initialized
How to Check If C++ Compiler Uses Ieee 754 Floating Point Standard
C++ Standard Library and Boehm Garbage Collector