Why I don’t like printf()
Join the DZone community and get the full member experience.
Join For Freehow many times have i seen this:
printf("hello world!\n");
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 hany: 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:
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.
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:
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 rally 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
Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Scaling Site Reliability Engineering (SRE) Teams the Right Way
-
Transactional Outbox Patterns Step by Step With Spring and Kotlin
-
Replacing Apache Hive, Elasticsearch, and PostgreSQL With Apache Doris
-
Never Use Credentials in a CI/CD Pipeline Again
Comments