How should I print types like off_t and size_t?

Viewed 153410

I'm trying to print types like off_t and size_t. What is the correct placeholder for printf() that is portable?

Or is there a completely different way to print those variables?

10 Answers

You can use z for size_t and t for ptrdiff_t like in

printf("%zu %td", size, ptrdiff);

But my manpage says some older library used a different character than z and discourages use of it. Nevertheless, it's standardized (by the C99 standard). For those intmax_t and int8_t of stdint.h and so on, there are macros you can use, like another answer said:

printf("value: %" PRId32, some_int32_t);
printf("value: %" PRIu16, some_uint16_t);

They are listed in the manpage of inttypes.h.

Personally, I would just cast the values to unsigned long or long like another answer recommends. If you use C99, then you can (and should, of course) cast to unsigned long long or long long and use the %llu or %lld formats respectively.

Which version of C are you using?

In C90, the standard practice is to cast to signed or unsigned long, as appropriate, and print accordingly. I've seen %z for size_t, but Harbison and Steele don't mention it under printf(), and in any case that wouldn't help you with ptrdiff_t or whatever.

In C99, the various _t types come with their own printf macros, so something like "Size is " FOO " bytes." I don't know details, but that's part of a fairly large numeric format include file.

Looking at man 3 printf on Linux, OS X, and OpenBSD all show support for %z for size_t and %t for ptrdiff_t (for C99), but none of those mention off_t. Suggestions in the wild usually offer up the %u conversion for off_t, which is "correct enough" as far as I can tell (both unsigned int and off_t vary identically between 64-bit and 32-bit systems).

If you use C11 or C18, you can use _Generic.

#include <stdio.h>
#include <sys/types.h>

    #define TYPE_FORMAT(variable) _Generic \
      (                                    \
        (variable)                         \
        , unsigned char       : "%hhu"     \
        , unsigned short      : "%hu"      \
        , unsigned int        : "%u"       \
        , unsigned long       : "%lu"      \
        , unsigned long long  : "%llu"     \
        , signed   char       : "%hhi"     \
        , signed   short      : "%hi"      \
        , signed   int        : "%i"       \
        , signed   long       : "%li"      \
        , signed   long long  : "%lli"     \
      )
      
      
int main(void)
  {
    off_t a=3321;
    printf(TYPE_FORMAT(a), a);
  }
      

However, there is one major drawback: You can not combine strings this way. Means this:

printf("Foo: " TYPE_FORMAT(a), a);

will not work, which makes it useless for many situation. Yes you could combine the string during runtime but that is annoying.

I saw this post at least twice, because the accepted answer is hard to remeber for me(I rarely use z or j flags and they are seems not platform independant).

The standard never says clearly the exact data length of size_t, so I suggest you should first check the length size_t on your platform then select one of them:

if sizeof(size_t) == 4 use PRIu32
if sizeof(size_t) == 8 use PRIu64

And I suggest using stdint types instead of raw data types for consistancy.

As I recall, the only portable way to do it, is to cast the result to "unsigned long int" and use %lu.

printf("sizeof(int) = %lu", (unsigned long) sizeof(int));
Related