tags:

views:

495

answers:

10

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.

(C, C++)

A: 

I've compiled it warning-free on a 32-bit machine with %lu.

Kristo
I suspect that doesn't always mean the printf implementation will handle it bug-free.
Mark B
A: 
#ifndef _LP64
    printf( "size = %u\n", x );
#else
    printf( "size = %lu\n", x );
#endif

Nasty but it works :)

Simone Margaritelli
casting to `unsigned long` is more portable and less ugly
Christoph
+1  A: 

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

nategoose
z is just a size specifier. You still need to add a conversion type like 'u' or 'd'.
swestrup
You are correct. Thanks for catching that.
nategoose
nategoose, you should delete or fix your answer (click edit), unless you want to get a random downvote from time to time.
avakar
+1  A: 

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
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
+10  A: 
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
AraK
There is "C++" tag. I think this is the best way in C++ :)
AraK
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
@Donal I wonder what kind of problem could C++ streams create in a C++ project!
AraK
@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
+1 streams is definitely the C++ way to do this.
Mark B
@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
@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
@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
+5  A: 

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.

T.J. Crowder
`z` is also supported by newlib (ie cygwin)
Christoph
+15  A: 

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
+1. Is this a C99 addition or does this apply to C++ as well (I don't have C90 handy)?
avakar
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
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
@Christoph: Nor is it in the latest draft, n3035.
GMan
@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
@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
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?
ArunSaha
@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
@Adam Rosenfield: Thanks.
ArunSaha
A: 

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
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
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
thanks for the comments, I've incorporated it in the reply
peterchen
+3  A: 

For C89: use %lu and cast to unsigned long:

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

For C99, use %zu:

size_t foo;
...
printf("foo = %zu\n", foo);
John Bode
+1  A: 

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
Probably want `%u` then.
GMan