I often experience situations where I want to print with printf
the value of an integer type of implementation-defined size (like ino_t
or time_t
). Right now, I use a pattern like this for this:
#include <inttypes.h>
ino_t ino; /* variable of unknown size */
printf("%" PRIuMAX, (uintmax_t)ino);
This approach works so far but it has a couple of disadvantages:
Is there a better strategy?
#include <inttypes.h>
ino_t ino; /* variable of unknown size */
/* ... */
printf("%" PRIuMAX, (uintmax_t)ino);
That will certainly work (with some provisos; see below), but I'd use:
printf("%ju", (uintmax_t)ino);
The j
length modifier
Specifies that a following
d
,i
,o
,u
,x
, orX
conversion specifier applies to anintmax_t
oruintmax_t
argument; or that a followingn
conversion specifier applies to a pointer to anintmax_t
argument.
There are also z
and t
modifiers for size_t
and ptrdiff_t
(and their corresponding signed/unsigned types), respectively.
And personally, I find the format string macros defined in <inttypes.h>
ugly and difficult to remember, which is why I prefer "%ju"
or "%jd"
.
As you mentioned, it's helpful to know whether the type (ino_t
in this case) is signed or unsigned. If you don't happen to know that, it's possible to figure it out:
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#define IS_SIGNED(type) ((type)-1 < (type)0)
#define DECIMAL_FORMAT(type) (IS_SIGNED(type) ? "%jd" : "%ju")
#define CONVERT_TO_MAX(type, value) \
(IS_SIGNED(type) ? (intmax_t)(value) : (uintmax_t)(value))
#define PRINT_VALUE(type, value) \
(printf(DECIMAL_FORMAT(type), CONVERT_TO_MAX(type, (value))))
int main(void) {
ino_t ino = 42;
PRINT_VALUE(ino_t, ino);
putchar('\n');
}
though that may be overkill. If you're sure the type is narrower than 64 bits, you can convert the value to intmax_t
, and the value will be preserved. Or you can use uintmax_t
and get well-defined results for all values, though printing -1
as 18446744073709551615
(264-1) may be a bit confusing.
All of this works only if your C implementation supports <stdint.h>
and the j
length modifier for printf
-- i.e., if it supports C99. Not all compilers do so (coughMicrosoftcough). For C90, the widest integer types are long
and unsigned long
, and you can convert to those and use "%ld"
and/or "%lu"
. You can theoretically test for C99 compliance using the __STDC_VERSION__
predefined macro -- though some pre-C99 compilers might still support types wider than long
and unsigned long
as an extension.