I've noticed quite a few hits to my printf formats specifications reference page, looking for information on how to print time_t, off_t and size_t types etc. However I realised the appropriate formats weren't immediately obvious from that page, and going by lots of code I've reviewed, it's a common confusion.

The main point to note is that the size of these types depend of various things, so outputting them portably is tricky. For example the size of off_t depends on the _FILE_OFFSET_BITS definition, and so one must cast it to the maximum int type supported to print it portably. Similarly time_t is usually dependent on the native integer size of the platform, but may even be represented as a float, so again one must cast it to the max int type before printing.

Now the C99 standard helps here as it defines new %z, %j, and %t formats for printing size_t, intmax_t, and ptrdiff_t sizes respectively. Previously one would have to do ugly casts to long long for example. C99 also specifies types and formats for a specific number of bits, like uint32_t and PRIu32 for example. The example program below shows how to portably output all these types and should work on all but obsolete systems. See also getlimits in coreutils.
#define __USE_MINGW_ANSI_STDIO 1 /* So mingw uses its printf not msvcrt */
#define _FILE_OFFSET_BITS 64
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <time.h>      /* for time_t */
#include <sys/types.h> /* for off_t */

#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
#define TYPE_MAX(t) \
  ((t) (! TYPE_SIGNED (t) \
        ? (t) -1 \
        : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))

int main(void)
{
    uint32_t uint32=0xffffFFFF;
    uintmax_t uintmax=UINTMAX_MAX;
    off_t offset=TYPE_MAX(off_t); /* Depends on _FILE_OFFSET_BITS */
    time_t time=TYPE_MAX(time_t); /* May be float! */
    size_t size=TYPE_MAX(size_t); /* Depends on int size */

    printf("native int bits %20zu %16x\n"
           "native long bits%20zu %16lx\n"
           "uint32_t max    %20"PRIu32" %16"PRIx32"\n"
           "uintmax_t max   %20ju %16jx\n" /* try PRIuMAX if %ju unsupported */
           "off_t max       %20jd %16jx\n" /* try PRIdMAX if %jd unsupported */
           "time_t max      %20jd %16jx\n"
           "size_t max      %20zu %16zx\n",
           sizeof(int)*CHAR_BIT, UINT_MAX,
           sizeof(long)*CHAR_BIT, ULONG_MAX,
           uint32, uint32,
           uintmax, uintmax,
           (intmax_t)offset, (intmax_t)offset,
           (intmax_t)time, (intmax_t)time,
           size, size);

#if defined __GNUC__ && defined _LP64
    unsigned __int128 ui128=TYPE_MAX(unsigned __int128);
    /* Note no way to specify constants > UINTMAX_MAX.
       Also printing is awkward and only feasible really for hex.  */
    printf("__int128 max    %20s %016"PRIx64"%016"PRIx64"\n", "",
            (uint64_t)(ui128>>64),(uint64_t)ui128);
#endif

    return 0;
}
On my 64 bit linux system, the output from the above program is:
native int bits                   32         ffffffff
native long bits                  64 ffffffffffffffff
uint32_t max              4294967295         ffffffff
uintmax_t max   18446744073709551615 ffffffffffffffff
off_t max        9223372036854775807 7fffffffffffffff
time_t max       9223372036854775807 7fffffffffffffff
size_t max      18446744073709551615 ffffffffffffffff
__int128 max                         ffffffffffffffffffffffffffffffff
Compiling on the same system in -m32 mode gives:
native int bits                   32         ffffffff
native long bits                  32         ffffffff
uint32_t max              4294967295         ffffffff
uintmax_t max   18446744073709551615 ffffffffffffffff
off_t max        9223372036854775807 7fffffffffffffff
time_t max                2147483647         7fffffff
size_t max                4294967295         ffffffff
© Jun 27 2008