Why I don’t like printf()

How many times have I seen this:

printf("Hello world!");

I have a strong opinion, and a rule for using printf():

“Do *not* use printf()!”

Using printf(), as the other ‘related’ functions like scanf(), can be very handy: it is easy to write something to the console, or to build a string. I used this in “printf() with the FRDM-KL25Z Board and without Processor Expert” too ;-). But in general it is bad. Really bad.

Code Size

Using printf() adds greatly to the code size of the application. I have seen cases where this is in the range of 10-20 KByte of code. The problem comes from the fact that printf(), as defined by the ANSI library standard, needs to support all the different format string. Including formatting octal numbers, floating point, etc. Even if you are not using octal numbers. Or when was the last time I used octal numbers? It is in there, and it adds up.

You might have a look at the printf() implementation of your library. If you have CodeWarrior, then have a look at

\lib\hc08c\src\printf.c

This is an implementation optimized for small memory systems, and tries hard to trim it down. The good thing is that you can disable a lot of stuff you do not need with this library implementation. That file is more than 1200 lines of source code just for printf().  Even with discounting the comments and empty lines, it is a *lot*. Yes, printf() does a lot of nice formatting things, but do I need it? Probably not. Do I need the fancy formatting strings I probably never will use? Nope. And one single usage of printf() can ruin my code size:

Printf Flash Size

Printf Flash Size

In this application, the usage of printf() vs. non-printf() functions has cut the code size by half. That 5K do not matter much if I have 1 MByte of FLASH. It matters if I have a smaller part say with 16 KByte or 32 KByte. Then using printf() is really a no-go.

But there are ways for improvement. For example this

printf("Hello world!\n");

is better written as

puts("Hello world!\n");

without the extra overhead.

💡 The Freescale HCS08 compiler even tries to replace calls to printf() with a call to puts() if enabled by an option. That’s cool, but better if the programmer is smarter. GCC can do this as well 🙂

RAM/Stack usage

The other negative impact of using printf(): it uses a lot of stack space. This because handling all the formatters plus the variable argument list has a price. It depends on the printf() implementation, but it easily adds 512-1024 bytes on the stack. If your application causes a stack overflow, it could be because of printf(). If you have multiple tasks in your system, then this easily adds up. Below is the same application from above, which shows the required RAM: using printf() has a severe impact too:

printf RAM usage

printf RAM usage

Again: if you have plenty of RAM available, wasting a few KBytes might not be a big deal. But it is again for a device with smaller amount of RAM. And engineers should not waste things 😉

Security

The other trap of printf() is: it is a constant source of programming errors.

For example what is wrong with

void printString(char *str) {
  printf(str);
}

?

💡 There is of course one problem (inherent to the way C handles strings) that the string is not properly zero terminated: then it would print the bytes until the first zero byte, potentially the entire memory 😦

The problem was that my application crashed in a really bad way. The problem was that the function received a string with ‘%’ in it:

printString("  %sssssssssss%\r\n");
printString("  %s  Value  s%\r\n");
printString("  %sssssssssss%\r\n");

The problem is that printf() will parse the ‘%’ and then will pop arguments from the stack which do not exist, causing a stack error. This easily leads to a system crash, or opens a program vulnerability (e.g. for hacker attack or security exploit).

The solution was to use this:

void printString(char *str) {
  printf("%s", str);
}

Additional special considerations and format string checking needs to be performed if the string can come from outside (e.g. user input), or gets constructed on the fly.

The GNU gcc has options as -Wall,-Wformat, -Wno-format-extra-args, -Wformat-security, -Wformat-nonliteral, but they only help at compile time, not for strings constructed at runtime.

Summary

printf() can cause a lot of troubles. It is adding to the code size, and it needs a lot of stack. Using printf() in ‘desktop’ applications or in applications where RAM/ROM footprint does not matter, is probably ok. But it is a security and stack overflow thread.

For smaller embedded systems where RAM/ROM footprint and stability is key, printf() and its variants should be banned from the application. Instead, safe string routines and simpler methods shall be used.

💡 I’m using the Utility module which has safe string manipulation routines: they all have the buffer size as an extra argument to avoid buffer overflow.

I agree that there is no need to re-invent the wheel, and printf() has a good side too. It is only that it should not be used in embedded system except for demonstration purposes ;-).

Happy Printing 🙂

Ceterum Censeo printf() Delendam Esse.”  🙂

Advertisements

28 thoughts on “Why I don’t like printf()

  1. I love your blog, and completely agree with you about avoiding printf. However, when you say:

    printf(“Hello world!\n”); is better written as puts(“Hello world!\n”);

    you might more accurate better saying:

    printf(“Hello world!\n”); is better written as puts(“Hello world!”);

    Don’t forget that “puts” adds its own newline.

    Like

  2. Pingback: Adding/Removing Floating Point Format for S08 Projects | MCU on Eclipse

    • Hi Bill,
      yes, exactly. I have seen 0x400 of stack needed even for non-GNU ‘optimized’ libraries for non-ARM processors. newlib is more something for a PC host machine where you have ‘megabytes’ of RAM available.

      Like

  3. Pingback: Tutorial: Using a Terminal Input and Output; *without* printf() and scanf() | MCU on Eclipse

  4. Pingback: Semihosting with Kinetis Design Studio | MCU on Eclipse

  5. Pingback: printf() and scanf() with GNU ARM Libraries | MCU on Eclipse

  6. Pingback: XFormat, a Lightweight printf() and sprintf() Alternative | MCU on Eclipse

  7. Pingback: Comparing CodeWarrior with Kinetis Design Studio | MCU on Eclipse

  8. Nonsense. If printf is too big just use a micro implementation. I’ve seen it many times: instead of printf people create their own string formatting routines which quickly turn into a ugly monster which is actually worse than printf! Just Google for micro printf and see that printf can be reduced to a ‘few bytes’.

    Like

    • It all depends what you need. In many cases even that micro printf is huge compared to simple implementation which just does what you need. The problem of printf is a) how it is usually implemented (very generic and lots of functions) and b) the consequence of that (usage of code space and stack space). In nearly all cases, the more esoteric features of printf are not needed, then a micro printf makes sense.

      Liked by 1 person

  9. Pingback: Using Kinetis Design Studio V3.0.0 with the Launchad 4.9-2015-q2 Release | MCU on Eclipse

  10. Pingback: GNU gcc printf() and BuiltIn Optimizations | MCU on Eclipse

  11. Pingback: GNU gcc printf() and BuiltIn Optimizations | Dinesh Ram Kali.

  12. Pingback: GNU gcc printf() and BuiltIn Optimizations -

  13. Pingback: Dealing with Code Size in Kinetis SDK v2.x Projects | MCU on Eclipse

  14. Pingback: Semihosting (again!) with NXP Kinetis SDK V2.0 | MCU on Eclipse

  15. I consider printf() to be something of a luxury. I had a project that started out on a very small HC08 and grew over the years and it did quite a lot of string formatting. It was a huge relief when I got to a point where I could use a stripped-down version of sprintf() on a larger MCU. It let me eliminate a ton of hard-to-read, special-purpose output code. If you’re using it enough for things other than debug output and you’re careful to select only the options you need, it can definitely justify the resources required.

    Like

  16. Pingback: Tutorial: RFID Tags with the NXP NFC Controller PN7120 and Eclipse | MCU on Eclipse

  17. Pingback: McuOnEclipse Components: 30-Oct-2016 Release | MCU on Eclipse

  18. Pingback: Using FreeRTOS with newlib and newlib-nano | MCU on Eclipse

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s