Learning something about printf, of all things…

May 16, 2009 | General | By: Mark VandeWettering

I’ve been C programming for… (quick arithmetic) roughly 25 years now, and yet, there are still things to learn. For instance, I decided to move my code for Milhouse back from my AMD64 Linux box to my Macbook for a little “mobile hacking” over the next week. I quickly found that unlike gcc on the Linux box, gcc on this Mac still thought that “long” variables were 32 bit. Various counters in Milhouse are 64 bit values, as are the hash values that are used in the transposition table, and I quickly found out that all the places where I previously used “%ld” as a format string had to be changed to “%lld”. Grumble!

You see, here’s the annoying thing about C: you know that shorts can hold char values, and ints can hold shorts, and longs can hold ints, but you actually don’t know how many bits any of these have without peeking using sizeof. Luckily, the C standard requires an include file sys/types.h which has typedefs which include types of various sizes, so if you really want a 32 bit unsigned int, you can use the type uint32_t and be reasonably sure that it will work. Such was the state of my knowledge a couple of days ago.

But here’s the thing: I didn’t know any way to generate the right format string for a particular size of data value. On my AMD box, %ld is used for 64 bit values. On my mac, I need to use %lld. Grumble!

But apparently this was all thought of by the C99 standards committee. They created an include file called inttypes.h which includes defines which tell you what format is needed. For example: PRIu64 is the code for a 64 bit unsigned integer value. On my mac, it expands to "ll" "u", which the C preprocessor is nice enough to cat together. Therefore, to print such a value, you need a line like:

printf("%" PRIu64 "\n", sixtyfourbitvalue) ;

Sure, it’s ugly. You think they would at least include the % in the macro. But, it does work. I’m tidying up all my code to play by these nice rules.

Comments

Comment from Tom Duff
Time 5/17/2009 at 11:13 am

They don’t include the % so that things like “%16” PRIu64 “\n” will work.

Comment from Mark VandeWettering
Time 5/17/2009 at 8:26 pm

Oh, sure, when you put it like that, it seems all so simple.

*blush*

Comment from Phil Howard
Time 5/20/2009 at 11:29 am

The solution to this that I have been using for 64 bit values for a few years is to always use %lld or %llu for the format, and always cast the argument to (long long) or (unsigned long long).

Comment from Pádraig Brady
Time 9/6/2009 at 5:02 pm

portable printf is often confusing. I wrote some notes on it:
http://www.pixelbeat.org/programming/gcc/int_types/

Comment from Minimiscience
Time 9/6/2009 at 6:15 pm

Obligatory pedantry: is specified by POSIX, not C99. int32_t and its relatives are defined in the standard C header file . Moreover, exact-width integer types for 8, 16, 32, and 64 bits are optional and are only present if the implementation actually has types with those widths; to be truly portable, you have to use int_least32_t and friends (also defined in ). See section 7.18 of the C99 standard for details.

Comment from Minimiscience
Time 9/6/2009 at 6:20 pm

The above comment was supposed to say that the header sys/types.h is specified by POSIX, while the types mentioned are defined in stdint.h. However, I put angle brackets around the header names, and apparently your blog software doesn’t escape special HTML characters. I now have an urge to try to break your site, but I’ll settle for blinking text instead.

Comment from mark
Time 9/6/2009 at 8:38 pm

More reasons to hate C.

The thing I hate most in C is actually the absolute path for header files.

This “feature” impacted EVERYTHING in the *nix world. It dictated the layout of the structure, and it STOPPED any alternative ideas (because anyone who would want to change that, would have to use another language, and we all know that this isn’t going to happen because C is actually USEABLE.)

After 25 years it shows you that a language can still surprise you?

Sounds as if you are too dumb, or the language sucks. :One of these must be true, and I think you are not dumb … >

Comment from Art
Time 9/6/2009 at 9:47 pm

I had to deal with this issue a few years back while working on some C code that had to print out a few 64bit values, and I distinctly remember coming across those #defines, and thinking that it was nifty. Thanks for reminding me about it!

Comment from Nathan
Time 9/7/2009 at 4:28 am

@Mark, regarding absolute paths for header files, you can usually set an include path that will tell the compiler where to look for the header files, so you don’t need an absolute path.

Comment from Oren Ben-Kiki
Time 9/7/2009 at 7:30 am

By leaving out the ‘%’ they allow you to write stuff like printf(“%04” PRIu64 “\n”, …) which would be otherwise impossible.

Comment from maht
Time 9/7/2009 at 9:22 am

@Mark blame the notion of relying on the disk to provide your namespace

Plan9 (hi Tom) did away with this notion. Slowly Lunix is coming rount (see man mount on Linux and look for the entry on bind – though the implementaion is lame (requires root, is global etc.))

http://plan9.bell-labs.com/magic/man2html/1/bind

Comment from Jared
Time 9/7/2009 at 11:05 am

The string concatenation you referenced doesn’t come from the C preprocessor. Instead it comes from the “string concatenation” phase of translation. See:
http://msdn.microsoft.com/en-us/library/bxss3ska%28VS.80%29.aspx

String concatenation comes out to be a pretty novel idea syntactically. Because a literal string’s type is a const char * (also written char const *), placing two strings next to each other has the effect of removing the null character at the end of the first literal string. Then the addresses of the first character up until 1 – the null character can represent the concatenated string.

Comment from Keith Thompson
Time 9/7/2009 at 3:06 pm

A quibble: For historic reasons, C string literals are not of type const char*.

In fact, a C string literal is of type char[N], where N is the length of the literal plus 1 (to allow for the terminating ”). For example, sizeof(“hello, world”) yields 13, not the size of a pointer. In most contexts, an expression of array type is implicitly converted to a pointer to the array’s first element, so string literals usually, but not always, “decay” to char*.

As for the “const”, it would make more sense for this to be applied to string literals, but it would have broken old code (pre-ANSI C didn’t have “const”, and code like func(“literal”) would have required adding “const” to the declaration of func). But attempting to modify a string literal invokes undefined behavior.

C++ was less concerned with backward compatibility, so C++ string literals are of type const char[N].

Pingback from Types and printf « /etc/shadow
Time 9/8/2009 at 10:43 am

[…] and printf By etcshadow Came across this post about printf-ing fixed-size types today and am very glad to have found macros for printf! Developing for the iPhone, with its […]