511

I have a variable of type size_t, and I want to print it using printf(). What format specifier do I use to print it portably?

In 32-bit machine, %u seems right. I compiled with g++ -g -W -Wall -Werror -ansi -pedantic, and there was no warning. But when I compile that code in 64-bit machine, it produces warning.

size_t x = <something>;
printf("size = %u\n", x);

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

The warning goes away, as expected, if I change that to %lu.

The question is, how can I write the code, so that it compiles warning free on both 32- and 64- bit machines?

Edit: As a workaround, I guess one answer might be to "cast" the variable into an integer that is big enough, say unsigned long, and print using %lu. That would work in both cases. I am looking if there is any other idea.

Arun
  • 19,750
  • 10
  • 51
  • 60
  • 4
    casting to `unsigned long` is the best option if your libc implementation doesn't support the `z` modifier; the C99 standard recommends `size_t` not to have an integer conversion rank greater than `long`, so you're reasonably safe – Christoph Mar 26 '10 at 16:10
  • possible duplicate of [Platform independent size_t Format specifiers in c?](http://stackoverflow.com/questions/2125845/platform-independent-size-t-format-specifiers-in-c/22114959#22114959) – maxschlepzig Mar 01 '14 at 13:29
  • 1
    [What's the correct way to use printf to print a size_t?](http://stackoverflow.com/q/940087/995714) – phuclv Aug 16 '15 at 09:19
  • 1
    On the Windows platform size_t can be larger than long. For compatibility reasons long is always 32-bit but size_t can be 64-bit. So, casting to unsigned long may lose half of the bits. Sorry :-) – Bruce Dawson Aug 07 '17 at 20:48
  • 3
    Possible duplicate of [What's the correct way to use printf to print a size\_t?](https://stackoverflow.com/questions/940087/whats-the-correct-way-to-use-printf-to-print-a-size-t) – phuclv Jan 30 '19 at 02:06

14 Answers14

596

Use the z modifier:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 15
    +1. Is this a C99 addition or does this apply to C++ as well (I don't have C90 handy)? – avakar Mar 26 '10 at 16:20
  • I believe this is a C99 addition, see §7.19.6.1.7. There's also `j` for `intmax_t`/`uintmax_t` and `t` for `ptrdiff_t`. – Adam Rosenfield Mar 26 '10 at 16:27
  • 11
    it's a C99 addition and not featured in the list of `printf()` length modifiers of the C++0x draft from 2009-11-09 (table 84 on page 672) – Christoph Mar 26 '10 at 16:28
  • 3
    @Christoph: Nor is it in the latest draft, n3035. – GManNickG Mar 26 '10 at 16:48
  • 14
    @avakar @Adam Rosenfield @Christoph @GMan: However, in n3035 §1.2 Normative references, only the C99 standard is referenced, and §17.6.1.2/3 of the same states "The facilities of the C standard library are provided." I would interpret this to mean that, unless otherwise specified, _everything_ in the C99 standard library is part of the C++0x standard library, including the additional format specifiers in C99. – James McNellis Mar 28 '10 at 02:49
  • 1
    @Christoph, `z` is not needed to be specified in table 84. That table is not a description of formats available to `printf` but merely a mapping from fundamental types to flags of `printf`. Since the streams are typesafe, you don't need to tell them with `z` that you print a `size_t`, they will figure out automatically when a `ulong` or `ulong long` etc.. is printed. – Johannes Schaub - litb Mar 28 '10 at 13:56
  • 1
    Forgive me for my lateness. I tried "%zu". It is fine as long as I am not using "-pedantic" in compilation. When I include that, the *error* is: ISO C++ does not support the 'z' gnu_printf length modifier. I am running gcc version 4.4.1 on solaris. Any thoughts? – Arun Apr 12 '10 at 23:22
  • 10
    @ArunSaha: It's a feature of only C99, not C++. If you want it to compile with `-pedantic`, you'll need to either get a compiler supporting the C++1x draft (highly unlikely), or you'll need to move your code into a file that's compiled as C99. Otherwise, your only option is to cast your variables to `unsigned long long` and use `%llu` to be maximally portable. – Adam Rosenfield Apr 13 '10 at 01:52
  • @LưuVĩnhPhúc whether `%zu` works is a function of your compiler and (especially) your library, not your operating system. It works fine for me with gcc 4.8.3 and the library that comes with Cygwin 1.7.32 (newlib). – David Conrad Oct 21 '14 at 19:46
  • 1
    @DavidConrad I know that it depends on the standard lib the compiler uses but it's too late to edit. Moreover, it doesn't work on mingw/mingw64 gcc. Cygwin uses a library for emulating Unix functions so obviously it should work – phuclv Oct 22 '14 at 03:35
  • 2
    you should add a link to a reference like http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html#Integer-Conversions – lindsaymacvean Sep 23 '15 at 16:54
  • While GCC 4.3.3 *does* support C99, in *Solaris 8* neither `%zu` nor `%Zu` work for me -- only `%u` can be used. Also, for *Sun WorkShop*, C99 is only [supported](http://www.oracle.com/technetwork/systems/ccompare-141326.html#upd2e) since version 6.2 (July 2001) – Bass Nov 18 '15 at 11:50
  • MSVC still doesn't fully support C99: this solution is only good for gnu-like compilers – avtomaton Apr 20 '16 at 10:41
  • 1
    @Adam Rosenfield: Visual Studio 2015 finally support %zu – KindDragon Aug 01 '16 at 16:23
  • 1
    `warning: unknown conversion type character 'z' in format [-Wformat=]` – Tomáš Zato Jan 13 '17 at 16:14
  • @AdamRosenfield in line 2 od code- no `ssize` existed, I think it is just a typo – Seyfi Nov 18 '18 at 11:01
  • The z portion is a length specifier which says the argument will be size_t in length. (source: https://stackoverflow.com/q/940087/2780334) – Jämes Dec 07 '18 at 12:26
  • 2
    `%zd` should really not be in that list. `%d` expects a *signed* integer, and `size_t` is by definition *unsigned*. – DevSolar Dec 28 '21 at 14:47
  • @DevSolar But he's not printing a `size_t` but an `ssize_t`, which is signed. – Jmb Sep 22 '22 at 10:41
  • @Jmb Ah, the [infamous ssize_t](https://rootdirectory.de/doku.php?id=negix:writeandsizet)... must have missed that while cross-reading. IMHO, that type should be killed with fire, but... ok. ;-) – DevSolar Sep 22 '22 at 11:05
101

Looks like it varies depending on what compiler you're using (blech):

...and of course, if you're using C++, you can use cout instead as suggested by AraK.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 3
    `z` is also supported by newlib (ie cygwin) – Christoph Mar 26 '10 at 16:36
  • 11
    `%zd` is incorrect for `size_t`; it's correct for the signed type corresponding to `size_t`, but `size_t` itself is an unsigned type. – Keith Thompson May 01 '13 at 00:14
  • 1
    @KeithThompson: I did mention `%zu` as well (and `%zx` in case they want hex). True enough that `%zu` should probably have been first in the list. Fixed. – T.J. Crowder May 01 '13 at 11:32
  • 12
    @T.J.Crowder: I don't think `%zd` should be in the list at all. I can't think of any reason to use `%zd` rather than `%zu` to print a `size_t` value. It's not even valid (has undefined behavior) if the value exceeds `SIZE_MAX / 2`. (For completeness, you might mention `%zo` for octal.) – Keith Thompson May 01 '13 at 14:49
  • Lol `%Iu` . Once again Microsoft does it their own way instead of just sticking with the standards. Took them until VS 2013 to finally include some c99 features. But I'm sure they have some kind of justification for their decisions. After all their programmers are way smarter than everyone else. :ppppp –  Dec 12 '15 at 16:19
  • @KeithThompson That type is called `ssize_t` on POSIX. – fuz May 13 '16 at 14:14
  • 3
    @FUZxxl: POSIX doesn't require that `ssize_t` is the signed type corresponding to `size_t`, so it's not guaranteed to match `"%zd"`. (It *probably* is on most implementations.) http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html – Keith Thompson May 13 '16 at 15:04
70

For C89, use %lu and cast the value to unsigned long:

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

For C99 and later, use %zu:

size_t foo;
...
printf("foo = %zu\n", foo);
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 7
    Considering 2013, suggest "For C99 and onward" & "For pre C99:". Best answer. – chux - Reinstate Monica Aug 10 '13 at 05:52
  • 10
    Do not do this. It will fail on 64 bit Windows where size_t is 64 bit and long is 32 bit. – Yttrill Oct 10 '15 at 06:08
  • 1
    @Yttrill: What's the answer for 64-bit windows, then? – John Bode Dec 16 '15 at 12:28
  • 1
    @JohnBode Perhaps `unsigned long long`? – James Ko Sep 02 '16 at 16:40
  • 2
    Or: you could cast to a `uint64_t` and then use the `PRIu64` macro from inttypes.h, which contains the format specifier. – James Ko Sep 02 '16 at 16:50
  • 3
    @JamesKo what's the point in that? `uint64_t` is C99, so if it's available then so is `"%zu"` (which is the correct way to do it). – Craig Barnes Aug 05 '18 at 14:00
  • 1
    @Yttrill: It will only fail if *the numerical value being printed* exceeds ulong_max. The fact that a value is of type `size_t` in no way implies its could represent a large numerical value quantity. For example, something like `printf("Ints take %u bytes.\n", (unsigned)sizeof (int);` could fail on a hypothetical platform where `int` has more than half a million padding bits, but realistically speaking there would be no advantage to outputting a type larger than `unsigned`, no matter how big `size_t` might be. – supercat Oct 05 '18 at 20:25
  • This (i.e. `%lu`) is the one for NVIDIA's nvc++. – user2023370 Feb 13 '22 at 10:42
9

Extending on Adam Rosenfield's answer for Windows.

I tested this code with on both VS2013 Update 4 and VS2015 preview:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015 generated binary outputs:

1
1
2

while the one generated by VS2013 says:

zu
zx
zd

Note: ssize_t is a POSIX extension and SSIZE_T is similar thing in Windows Data Types, hence I added <BaseTsd.h> reference.

Additionally, except for the follow C99/C11 headers, all C99 headers are available in VS2015 preview:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

Also, C11's <uchar.h> is now included in latest preview.

For more details, see this old and the new list for standard conformance.

vulcan raven
  • 32,612
  • 11
  • 57
  • 93
8
printf("size = %zu\n", sizeof(thing) );
haccks
  • 104,019
  • 25
  • 176
  • 264
nategoose
  • 12,054
  • 27
  • 42
8

In any reasonably modern C implementation, "%zu" is the correct way to print a value of type size_t:

printf("sizeof (int) = %zu\n", sizeof (int));

The "%zu" format specifier was added in the 1999 ISO C standard (and adopted by the 2011 ISO C++ standard). If you don't need to be concerned about implementations older than that, you can stop reading now.

If your code needs to be portable to pre-C99 implementations, you can cast the value to unsigned long and use "%lu":

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

That's not portable to C99 or later, because C99 introduced long long and unsigned long long, and therefore the possibility that size_t is wider than unsigned long.

Resist the temptation to use "%lu" or "%llu" without the cast. The type used to implement size_t is implementation-defined, and if the types don't match, the behavior is undefined. Something like printf("%lu\n", sizeof (int)); might "work", but it's not at all portable.

In principle, the following should cover all possible cases:

#if __STDC_VERSION__ < 199901L
    printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
#else
    printf("sizeof (int) = %zu\n", sizeof (int));
#endif

In practice, it might not always work correctly. __STD_VERSION__ >= 199901L should guarantee that "%zu" is supported, but not all implementations are necessarily correct, especially since __STD_VERSION__ is set by the compiler and "%zu" is implemented by the runtime library. For example, an implementation with partial C99 support might implement long long and make size_t a typedef for unsigned long long, but not support "%zu". (Such an implementation likely wouldn't define __STDC_VERSION__.)

It's been pointed out that Microsoft's implementation can have 32-bit unsigned long and 64-bit size_t. Microsoft does support "%zu", but that support was added relatively late. On the other hand, casting to unsigned long will be a problem only if the particular size_t value happens to exceed ULONG_MAX, which is unlikely to happen in practice.

If you're able to assume reasonably modern implementations, just use "%zu". If you need to allow for older implementations, here's an absurdly portable program that adapts to various configurations:

#include <stdio.h>
#include <limits.h>
int main(void) {
    const size_t size = -1; /* largest value of type size_t */
#if __STDC_VERSION__ < 199901L
    if (size > ULONG_MAX) {
        printf("size is too big to print\n");
    }
    else {
        printf("old: size = %lu\n", (unsigned long)size);
    }
#else
    printf("new: size = %zu\n", size);
#endif
    return 0;
}

One implementation that prints "size is too big to print" (x86_64-w64-mingw32-gcc.exe -std=c90 on Windows/Cygwin) actually supports unsigned long long as an extension on top of C90, so you might be able to take advantage of that -- but I can imagine a pre-C99 implementation that supports unsigned long long but doesn't support "%llu". And that implementation supports "%zu" anyway.

In my experience, I've only wanted to print size_t values in quick throwaway code when I'm exploring an implementation rather than in production code. In that kind of context, it's probably sufficient just to do whatever works.

(The question is about C, but I'll mention that in C++ std::cout << sizeof (int) will work correctly in any version of the language.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
7

For those talking about doing this in C++ which doesn't necessarily support the C99 extensions, then I heartily recommend boost::format. This makes the size_t type size question moot:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

Since you don't need size specifiers in boost::format, you can just worry about how you want to display the value.

swestrup
  • 4,079
  • 3
  • 22
  • 33
5
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • 11
    Yeah, but the questioner asks specifically for a `printf` specifier. I'd guess that they have some other unstated constraints that make using `std::cout` a problem. – Donal Fellows Mar 26 '10 at 16:14
  • 1
    @Donal I wonder what kind of problem could C++ streams create in a C++ project! – Khaled Alshaya Mar 26 '10 at 16:38
  • 11
    @AraK. They are very slow? They add a LOT of bytes for not much reason. ArunSaha just wants to know for his/her own personal knowledge? Personal preference (I prefer stdio to fstream myself). There are many reasons. – KitsuneYMG Mar 26 '10 at 16:58
  • @Donal: Actually, when AraK posted his answer, the question *didn't* specifically say he wanted to use `printf`. He said he wanted to output the value, and he used a `printf` example, but the door was open... :-) The question has since been clarified. – T.J. Crowder Mar 26 '10 at 17:37
  • 2
    @T.K.Crowder: Well, the original request did say that a C solution was wanted (through tagging) and there are good reasons to not use streams in C++, e.g., if the output format descriptor is being pulled from a message catalog. (You could write a parser for messages and use streams if you wanted, but that's a lot of work when you can just leverage existing code.) – Donal Fellows Mar 26 '10 at 22:41
  • 1
    @Donal: The tags were C and C++. I'm not in any way advocating C++'s I/O stream stuff (I'm not a fan of it), just pointing out that the question *didn't* originally *"...ask specification for a `printf` specifier." – T.J. Crowder Mar 27 '10 at 06:33
2

As AraK said, the c++ streams interface will always work portably.

std::size_t s = 1024; std::cout << s; // or any other kind of stream like stringstream!

If you want C stdio, there is no portable answer to this for certain cases of "portable." And it gets ugly since as you've seen, picking the wrong format flags may yield a compiler warning or give incorrect output.

C99 tried to solve this problem with inttypes.h formats like "%"PRIdMAX"\n". But just as with "%zu", not everyone supports c99 (like MSVS prior to 2013). There are "msinttypes.h" files floating around to deal with this.

If you cast to a different type, depending on flags you may get a compiler warning for truncation or a change of sign. If you go this route pick a larger relevant fixed size type. One of unsigned long long and "%llu" or unsigned long "%lu" should work, but llu may also slow things down in a 32bit world as excessively large. (Edit - my mac issues a warning in 64 bit for %llu not matching size_t, even though %lu, %llu, and size_t are all the same size. And %lu and %llu are not the same size on my MSVS2012. So you may need to cast + use a format that matches.)

For that matter, you can go with fixed size types, such as int64_t. But wait! Now we're back to c99/c++11, and older MSVS fails again. Plus you also have casts (e.g. map.size() is not a fixed size type)!

You can use a 3rd party header or library such as boost. If you're not already using one, you may not want to inflate your project that way. If you're willing to add one just for this issue, why not use c++ streams, or conditional compilation?

So you're down to c++ streams, conditional compilation, 3rd party frameworks, or something sort of portable that happens to work for you.

Rick Berge
  • 474
  • 1
  • 4
  • 14
1

In most contexts where a programmer would want to output a size_t, the programmer would have a reasonable upper bound on the numerical value being output. If a programmer is e.g. outputting a message saying how large an int is, using:

printf("int is %u bytes", (unsigned)sizeof (int) );

would be for all practical purposes just as portable as, but possibly faster and smaller than:

printf("int is %zu bytes", sizeof (int) );

The only situation where such a construct could fail would be on a platform where the number of bytes worth of padding on an int is absurdly big relative to the magnitude of the largest value an unsigned int can represent (it's somewhat implausible that sizeof (int) could be larger than 65535, but even more implausible that it could be that big without unsigned having enough value bits to represent a number that's bigger than sizeof (int).

supercat
  • 77,689
  • 9
  • 166
  • 211
-1

Will it warn you if you pass a 32-bit unsigned integer to a %lu format? It should be fine since the conversion is well-defined and doesn't lose any information.

I've heard that some platforms define macros in <inttypes.h> that you can insert into the format string literal but I don't see that header on my Windows C++ compiler, which implies it may not be cross-platform.

Kylotan
  • 18,290
  • 7
  • 46
  • 74
  • 1
    Most compilers will not warn you if you pass something of the wrong size into printf. GCC is an exception. inttypes.h was defined in C99 so any C compiler that is C99 compliant will have it, which should be all of them by now. Still, you may have to turn C99 on with a compiler flag. In any case, intttypes.h doesn't define a specific format for size_t or ptrdiff_t, since they were decided to be important enough to get their own size specifiers of 'z' and 't' respectively. – swestrup Mar 26 '10 at 16:22
  • 1
    If you use `%lu`, you should cast the `size_t` value to `unsigned long`. There is no implicit conversion (other than promotions) for arguments to `printf`. – Keith Thompson May 01 '13 at 00:15
-2

C99 defines "%zd" etc. for that. (thanks to the commenters) There is no portable format specifier for that in C++ - you could use %p, which woulkd word in these two scenarios, but isn't a portable choice either, and gives the value in hex.

Alternatively, use some streaming (e.g. stringstream) or a safe printf replacement such as Boost Format. I understand that this advice is only of limited use (and does require C++). (We've used a similar approach fitted for our needs when implementing unicode support.)

The fundamental problem for C is that printf using an ellipsis is unsafe by design - it needs to determine the additional argument's size from the known arguments, so it can't be fixed to support "whatever you got". So unless your compiler implement some proprietary extensions, you are out of luck.

peterchen
  • 40,917
  • 20
  • 104
  • 186
  • 3
    the `z` size modidfier is standard C, but some libc implementations are stuck in 1990 for various reasons (eg Microsoft basically abandoned C in favour of C++ and - more recently - C#) – Christoph Mar 26 '10 at 16:16
  • 3
    C99 defined the size specifier 'z' to be the size of a size_t value, and 't' to be the size of a ptrdiff_t value. – swestrup Mar 26 '10 at 16:24
  • 3
    `%zd` is wrong, it is unsigned so it should be `%zu`. – David Conrad Oct 21 '14 at 19:50
-3

On some platforms and for some types there are specific printf conversion specifiers available, but sometimes one has to resort to casting to larger types.

I've documented this tricky issue here, with example code: http://www.pixelbeat.org/programming/gcc/int_types/ and update it periodically with info on new platforms and types.

pixelbeat
  • 30,615
  • 9
  • 51
  • 60
  • 2
    Note that link-only answers are discouraged, SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference. – kleopatra Jul 23 '13 at 09:34
-9

if you want to print the value of a size_t as a string you can do this:

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

result is:

number: 2337200120702199116

text: Lets go fishing in stead of sitting on our but !!

Edit: rereading the question because of the down votes i noted his problem is not %llu or %I64d but the size_t type on different machines see this question https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/

size_t is unsigned int on a 32bit machine and unsigned long long int on 64bit
but %ll always expects a unsigned long long int.

size_t varies in length on different operating systems while %llu is the same

Andre
  • 172
  • 2
  • 8
  • casting the first 8 bytes of the char array to a unsigned long long 64bit through the size_t pointer and print them as number with the printf %I64d isn't really spectacular i know, of course i didn't at code to prevent type overflow but that's not in the scope of the question. – Andre Jun 05 '17 at 11:30