Reputation: 386621
In variadic functions, default argument promotions occur.
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 todouble
. These are called the default argument promotions. [...]
6.5.2.2.7 [...] The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
Therefore,
signed char c = 123;
int i = 123;
float f = 123;
double d = 123;
printf("%d\n", i); // ok
printf("%d\n", c); // ok, even though %d expects int.
printf("%f\n", d); // ok
printf("%f\n", f); // ok, even though %f expects double.
So why is there a printf
length modifier for char
(hh
) and short
(h
)?
Section number refer to N2176.
Upvotes: 0
Views: 652
Reputation: 12708
They are not there for printf()
usage, but for scanf()
to be able to use references to short
integers and char
integers. For uniformity and completeness, they are accepted for printf()
functions, but they are undistinguisable, as the vaarg parameters of printf()
are promoted to int
for all parameters that are of types short
and char
integer values. So they are equivalent in printf()
but not in scanf()
and friends.
Upvotes: 0
Reputation: 215090
Regarding the hh
specifier specifically, it was explicitly added in C99 in order to utilize printing of all the default fixed size types from stdint.h
/inttypes.h
. C99 mades the types int_leastn_t
from 8 to 64, mandatory, so there was a need for corresponding format specifiers.
From the C99 rationale 5.10, §7.19.6.1 (fprintf
):
The
%hh
and%ll
length modifiers were added in C99 (see §7.19.6.2).
§7.19.6.2 (fscanf
):
A new feature of C99: The
hh
andll
length modifiers were added in C99.ll
supports the new long long int type.hh
adds the ability to treat character types the same as all other integer types; this can be useful in implementing macros such asSCNd8
in<inttypes.h>
(see 7.18).
Before C99, there was just d
, h
and l
for printing integer types. In C99, a conventional implementation could for example define inttypes.h
specifiers as:
#define SCNi8 hh
#define SCNi16 h
#define SCNi32 d
#define SCNi64 ll
And now the default argument promotions becomes the head ache of the printf
/scanf
implementation, rather than the inttypes.h
implementation.
Upvotes: 0
Reputation: 1
It's for backwards compatibility.
In a draft version of the C89 standard, printing a signed int
, short
or char
with the %x
format specifier is not undefined behavior:
d, i, o, u, x, X The int argument is converted to signed decimal ( d or i ), unsigned octal ( o ), unsigned decimal ( u ), or unsigned hexadecimal notation ( x or X ); the letters abcdef are used for x conversion and the letters ABCDEF for X conversion. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it will be expanded with leading zeros. The default precision is 1. The result of converting a zero value with an explicit precision of zero is no characters.
This seems to document that pre-standardized C, using format specifiers such as %x
for signed values was an existing practice, thus a preexisting code base using h
and hh
length modifiers likely existed.
Without the h
and hh
length modifiers, signed char
values with a bit pattern 0xFF
would be printed on a 32-bit-int
system as 0xFFFFFFFF
if printed with a simple %X
format specifier.
Upvotes: 1
Reputation: 141648
Consider this example:
#include <stdio.h>
int main(void)
{
unsigned short x = 32770;
printf("%d\n", x) ; // (1)
printf("%u\n", x) ; // (2)
}
On a typical 16-bit implementation, the default argument promotions take unsigned short
to unsigned int
, whereas on a typical 32-bit implementation, unsigned short
becomes int
.
So on the 16-bit system (1) is UB and (2) is correct, but on the 32-bit system, (1) is correct and (2) it can be debated whether correct or UB.
Using %hu
for printing x
works on all systems and you don't have to think about these issues.
A similar example could be constructed for char
on systems with sizeof(int) == 1
.
Upvotes: 7