Yes, Virginia. You CAN printf a size_t! And pointers.

I always learn from comments. Sadly, I don’t mean the comments inside the million lines of code I maintain for my day job — they usually don’t exist ;-)

I have had two previous posts dealing with sizeof() being used on a string constant like this:

#define VERSION "1.0.42-beta"
printf ("sizeof(VERSION) = %d\n", sizeof(VERSION));

Several comments were left to make this more better.

Use %z to print a size_t

The first pointed out that sizeof() is not returning a %d integer:

sizeof does not result in an int, so using %d is not correct.

– F W

Indeed, this code should generate a compiler warning on a good compiler. I would normally cast the sizeof() return value to an int like this:

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

BUT, I knew that really wasn’t a solution since that code is not portable. An int might be 16-bits, 32-bits or 64-bits (or more?) depending on the system architecture. I often write test code on a PC using Code::Blocks which uses the GNU-C compiler. On that system, I would need to use “%ld” for a long int. When that code is used on an embedded compiler (such as the CCS compiler for PIC42 chips), I need to make that “%d”.

I just figured printf() pre-dates stuff like that and thus you couldn’t do anything about it.

But now I know there is a proper solution — if you have a compiler that supports it. In the comments again…

… when you want to print a size_t value, using %zu.

– Sean Patrick Conner

Thank you, Sean Patrick Conner! You have now given me new information I will use from now on. I was unaware of %z. I generally use the website www.cplusplus.com to look up C things, and sure enough, on the printf entry it mentions %z — just in a separate box below the one I always look at. I guess I’d never scrolled down.

cplusplus.com/reference/cstdio/printf/

This old dog just learned some new tricks!

int var = 123;

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

Thank you very much for pointing this out to me. Unfortunately, the embedded compiler I use for my day job does not support any of the new stuff, and only has a sub-set of printf, but the Windows compiler I use for testing does.

Bonus: printing pointers for fun and profit

I’d previously ran in to this when trying to print out a pointer:

int main()
{
    char *ptr = 0x12345678;
    
    printf ("ptr = 0x%x\n", ptr);

    return EXIT_SUCCESS;
}

A compiler should complain about that, like this:

warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char *’ [-Wformat=]

…so I’d just do a bit of casting, to cast the pointer to what %x expects:

printf ("ptr = 0x%x\n", (unsigned int)ptr);

BUT, that assumes an “int” is a certain size. This casting might work find on a 16-bit Arduino, then need to be changed for a 32-bit or 64-bit PC program.

And, the same needs to be done when trying to assign a number (int) to a char pointer. This corrects both issues, but does so the incorrect way:

int main()
{
    char *ptr = (char*)0x12345678;

    printf ("ptr = 0x%lx\n", (unsigned long)ptr);

    return EXIT_SUCCESS;
}

First, I had to cast the number to be a character pointer, else it would not assign to “char *ptr” without a warning.

Second, since %x expects an “unsigned int”, and pointers on this sytem are long, I had to change the printf to use “%lx” for a long version of %x, and cast the “ptr” itself to be an “unsigned long”.

Had I written this initially on a system that uses 16-bit ints (like Arduino, PIC24, etc.), I would have had to do it differently, casting things to “int” instead of “long.”

This always drove me nuts, and one day I wondered if modern C had a way to deal with this. And, indeed, it does: %p

This was something that my old compilers either didn’t have, or I just never learned. I only discovered this within the past five years at my current job. It solves the problems by handling a “pointer” in whatever size it is for the system the code is compiled on. AND it even includes the “0x” prefix in the output:

int main()
{
    char *ptr = (char*)0x12345678;

    printf ("ptr = %p\n", ptr);

    return EXIT_SUCCESS;
}

I suppose when I found there was a “real” way to print pointers I should have expected there was also a real way to print size_t … but it took you folks to teach me that.

And I thank you.

Until next time…

7 thoughts on “Yes, Virginia. You CAN printf a size_t! And pointers.

  1. MiaM

    Although technically not great to cast size_t to int, imagine the poor soul that has to read source code that contains a string constant that is so long that it’s size can’t be correctly casted to a 16-bit (signed or unsigned) integer. 32767 or 65535 characters of pure gold? :D

    (Are there any C-like compilers (they wouldn’t be actually conforming to the C standard AFAIK, thus C-like) that has integers that has less than 16 bits)?

    Reply
    1. Allen Huffman Post author

      I doubt it! The C standard says an int is “at least 16-bits” so even the 1980s CoCo C compiler from Microware used 16-bit ints. So my question is, on machines without 16-bit registers, how do they do it? Fake the 16 in software? Where there Commodore C64 C compilers?

      Reply
  2. MiaM

    It seems like the anti spam thingie has a hard time allowing my comments? Not sure if it’s a bug / slowness or what?

    Either way, if what I wrote isn’t just waiting for manual approval, I make another attempt:

    Imagine the poor soul who has to read a source code file with sting constant that won’t fit if size_t is casted to a 16-bit integer. 32766 or 65534 characters of famtastic comtemt :D :D

    Reply
          1. MiaM

            It could also be a browser issue. I’m using Firefox which apparently is only used by a small minority nowadays.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.