Hello everyone,
In my code, I use uint16_t (exactly 16-bit-wide unsigned integer type)
and I need to print their value in base 10.
http://www.opengroup.org/onlinepubs/009695399/basedefs/inttypes.h.html
As far as I understand, the recommended method is:
#include <inttypes.h>
#include <stdio.h>
int main(void)
{
uint16_t val = 1234;
printf("VALUE = %" PRIu16 "\n", val);
return 0;
}
Is this correct?
On my platform (Linux/IA-32/GCC/glibc) inttypes.h provides the
following definitions:
# define PRIu8 "u"
# define PRIu16 "u"
# define PRIu32 "u"
# define PRIu64 __PRI64_PREFIX "u"
I expected PRIu16 to be "hu". Is it "u" because, on my platform,
16-bit values are automatically promoted to 32-bit values, which
might make "u" and "hu" equivalent?
Regards.
First, one relevant factor here is that printf takes a variable number
of arguments.
Looking at n1124.pdf (which I believe, but don't know for certain, is
the same as the C99 standard in this case):
section 6.5.2.2, paragraph 7:
# If the expression that denotes the called function has a type that
does include a prototype,
# the arguments are implicitly converted, as if by assignment, to the
types of the
# corresponding parameters, taking the type of each parameter to be
the unqualified version
# of its declared type. 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.
These default argument promotions are defined in 6.5.2.2, 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.
Finally, the integer promotions are defined in section 6.3.1.1,
paragraph 2:
# If an int can represent all values of the original type, the value
is converted to an int;
# otherwise, it is converted to an unsigned int. These are called the
integer
# promotions. All other types are unchanged by the integer promotions.
(Some extra context is needed here; this applies not in all cases, but
in some predefined cases; the relevant one here is "An object or
expression with an integer type whose integer conversion rank is less
than or equal to the rank of int and unsigned int.")
I'm guessing that your system has a 32-bit int and a 16-bit short. In
this case, an int can represent all values a unsigned short can, and
an unsigned short has an integer conversion rank less than that of an
unsigned int, so the default argument promotions will promote the
unsigned short argument (a 'trailing argument') to a (signed) int
before the function is called, so printf will see an int whenever it's
given an unsigned short as its argument. I'm also guessing that in
this case, uint16_t is unsigned short, so your uint16_t argument will
be promoted to an int. Finally, section 7.15.1.1, paragraph 2, allows
as as an exception to the rule that the argument's actual type (int,
in this case) must be compatible with the type given when reading an
argument using va_arg:
# one type is a signed integer type, the other type is the
corresponding unsigned integer
# type, and the value is representable in both types
So your uint16_t is promoted to a (signed) int when you call printf;
printf reads this value as an unsigned int due to the %u specifier,
but it's allowed to do this because all values representable in a
uint16_t are representable as unsigned ints and unsigned int is the
corresponding unsigned integer type to int, and so the %u specifier is
correct. (The same thing would happen if you just passed an unsigned
short to printf directly; printf would have to use a signed or
unsigned int to read back its value, so %hu could be defined to do the
same as %u in all cases and still be standard-complaint, as far as I
can tell.)
Note that all this only happens because of the ... in printf's
prototype; if printf always took an unsigned short (or a uint16_t) as
an argument and its prototype said that, then it would get an unsigned
short as its parameter, not an int.
To answer your first question, placing PRIu16 into printf's format
string is exactly the right thing to do, as your header files take
care of all these complicated integer promotions for you, and that's
what the PRIu16 macro is for in the first place.