128

I have the following

size_t   i = 0;
uint32_t k = 0;

printf("i [ %lu ] k [ %u ]\n", i, k);

I get the following warning when compiling:

format ‘%lu’ expects type ‘long unsigned int’, but argument has type ‘uint32_t’

When I ran this using splint I got the following:

Format argument 1 to printf (%u) expects unsigned int gets size_t: k

Many thanks for any advice,

jww
  • 97,681
  • 90
  • 411
  • 885
ant2009
  • 27,094
  • 154
  • 411
  • 609

4 Answers4

160

Try

#include <inttypes.h>
...

printf("i [ %zu ] k [ %"PRIu32" ]\n", i, k);

The z represents an integer of length same as size_t, and the PRIu32 macro, defined in the C99 header inttypes.h, represents an unsigned 32-bit integer.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Compiles with no warnings. But splint doesn't like it. Unrecognized format code: %zu. And for the second one. Parse Error. (For help on parse errors, see splint -help parseerrors.) *** Cannot continue. – ant2009 Jul 02 '10 at 18:59
  • 4
    @robUK: Heh. I suggest you file a bug for splint. – kennytm Jul 02 '10 at 19:04
  • 10
    This is the right answer. Though my personal recommendation is to simply cast, e.g. `printf( "%lu", (unsigned long )i )`. Otherwise one ends up later with pile of warnings all over the code due to a type change. – Dummy00001 Jul 02 '10 at 19:54
  • 1
    This is the correct answer. I agree with KennyTM about filing a bug for splint. By the way, "%zu" is the proper format for size_t. You don't need any of the PRI* macros for printing size_t. – R.. GitHub STOP HELPING ICE Jul 03 '10 at 14:08
  • 1
    If I remember correctly %zu is C99, and in the question he wrote 'C89'. – alcor Oct 15 '13 at 11:37
  • 9
    @alcor yes he did put C89 (apparently the gcc compiler flag he's using) but he is using `uint32_t` so actually it is C99 code, and should be compiled as such. – Colin D Bennett Oct 24 '13 at 16:32
40

Sounds like you're expecting size_t to be the same as unsigned long (possibly 64 bits) when it's actually an unsigned int (32 bits). Try using %zu in both cases.

I'm not entirely certain though.

Cogwheel
  • 22,781
  • 4
  • 49
  • 67
  • 1
    No warnings when compiling. However, running splint I get the following: 1) printf (%u) expects unsigned int gets uint32_t: i 2) printf (%u) expects unsigned int gets size_t: k – ant2009 Jul 02 '10 at 18:45
  • Sounds like splint is just being pedantic, then. It's probably going off the names of types in the source code and doesn't realize they're equivalent. I wonder what it would do with @KennyTM's answer... It certainly should be more portable. – Cogwheel Jul 02 '10 at 18:49
  • 3
    splint is actually doing the right thing. Just because `int32_t` happens to be `int` on your compiler/platform doesn't mean it might not be `long` on another. Same for `size_t`. It's actually going out of its way and doing **more** work to detect this portability bug since the easy, natural check would be to just honor the typedef like the compiler does. – R.. GitHub STOP HELPING ICE Jul 03 '10 at 17:18
  • 5
    -1, sorry it's not portable. All that's needed is that the format specifiers and the types agree, and you can always cast to make that true. long is at least 32bits, so `%lu` together with `(unsigned long)k` is always correct. `size_t` is trickier, which is why `%zu` was added in C99. If you can't use that, then treat it just like `k` (`long` is the biggest type in C89, `size_t` is very unlikely to be larger). – u0b34a0f6ae Nov 08 '11 at 21:32
33

All that's needed is that the format specifiers and the types agree, and you can always cast to make that true. long is at least 32 bits, so %lu together with (unsigned long)k is always correct:

uint32_t k;
printf("%lu\n", (unsigned long)k);

size_t is trickier, which is why %zu was added in C99. If you can't use that, then treat it just like k (long is the biggest type in C89, size_t is very unlikely to be larger).

size_t sz;
printf("%zu\n", sz);  /* C99 version */
printf("%lu\n", (unsigned long)sz);  /* common C89 version */

If you don't get the format specifiers correct for the type you are passing, then printf will do the equivalent of reading too much or too little memory out of the array. As long as you use explicit casts to match up types, it's portable.

u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
20

If you don't want to use the PRI* macros, another approach for printing ANY integer type is to cast to intmax_t or uintmax_t and use "%jd" or %ju, respectively. This is especially useful for POSIX (or other OS) types that don't have PRI* macros defined, for instance off_t.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711