How can one print a size_t variable portably using

2018-12-31 14:31发布

问题:

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: I guess one answer might be to \"cast\" the variable into an unsigned long, and print using %lu. That would work in both cases. I am looking if there is any other idea.

回答1:

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


回答2:

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

  • gnu says %zu (or %zx, or %zd but that displays it as though it were signed, etc.)
  • Microsoft says %Iu (or %Ix, or %Id but again that\'s signed, etc.) — but as of cl v19 (in Visual Studio 2015), Microsoft supports %zu (see this reply to this comment)

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



回答3:

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);


回答4:

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.



回答5:

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


回答6:

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.



回答7:

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


回答8:

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.



回答9:

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.



回答10:

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.



回答11:

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.



回答12:

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



标签: c printf