I like Jonathan Leffler's answer, but I thought I'd pipe up with some technical details, for those who intend to write a portable library or something providing an API with variadic functions, and thus need to delve in to the details.
Variadic parameters are subject to default argument promotions (C11 draft N1570 as PDF; section 6.5.2.2 Function calls, paragraph 6):
.. 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.
[If] .. 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:
one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or void
Floating-point constants are of type double
, unless they are suffixed with f
or F
(as in 1.0f
), in which case they are of type float
.
In C99 and C11, integer constants are of type int
if they fit in one; long
(AKA long int
) if they fit in one otherwise; of long long
(AKA long long int
) otherwise. Since many compilers assume an integer constant without a size suffix is a human error or typo, it is a good practice to always include the suffix if the integer constant is not of type int
.
Integer constants can also have a letter suffix to denote their type:
u
or U
for unsigned int
l
or L
for long int
lu
or ul
or LU
or UL
or lU
or Lu
or uL
or Ul
for unsigned long int
ll
or LL
or Ll
or lL
for long long int
llu
or LLU
(or ULL
or any of their uppercase or lowercase variants) for unsigned long long int
The integer promotion rules are in section 6.3.1.1.
To summarize the default argument promotion rules for C11 (there are some additions compared to C89 and C99, but no significant changes):
float
are promoted to double
All integer types whose values can be represented by an int
are promoted to int
. (This includes both unsigned and signed char
and short
, and bit-fields of types _Bool
, int
, and smaller unsigned int
bit-fields.)
All integer types whose values can be represented by an unsigned int
(but not an int
) are promoted to unsigned int
. (This includes unsigned int
bit fields that cannot be represented by an int
(of CHAR_BIT * sizeof (unsigned int)
bits, in other words), and typedef'd aliases of unsigned int
, but that's it, I think.)
Integer types at least as large as int
are unchanged. This includes types long
/long int
, long long
/long long int
, and size_t
, for example.
There is one 'gotcha' in the rules that I'd like to point out: "signed to unsigned is okay, unsigned to signed is iffy":
If the argument is promoted to a signed integer type, but the function obtains the value using the corresponding unsigned integer type, the function obtains the correct value using modulo arithmetic.
That is, negative values will be as if they were incremented by (1 + maximum representable value in the unsigned integer type), making them positive.
If the argument is promoted to an unsigned integer type, but the function obtains the value using the corresponding signed integer type, and the value is representable in both, the function obtains the correct value. If the value is not representable in both, the behaviour is implementation-defined.
In practice, almost all architectures do the opposite of above, i.e. the signed integer value obtained matches the unsigned value substracted by (1 + the largest representable value of the unsigned integer type). I've heard that some strange ones may signal integer overflow or something similarly weird, but I have never gotten my mitts on such machines.
The man 3 printf man page (courtesy of the Linux man pages project) is quite informative, if you compare the above rules to printf specifiers. The make_message()
example function at the end (C99, C11, or POSIX required for vsnprintf()
) should also be interesting.