printf("Hello world, it's time to reexamine printf()");
From "Hello World" to the latest Linux kernel, printf molds the C programmer's mindset on formatted output. Love it or hate it, printf's influence has spawned numerous ports that unquestioningly copy its syntax and semantics in C libraries and even scripting languages. How printf earned this distinction through various strengths:
Templating is perhaps the most straightforward way to produce formatted output. The popularity of PHP and other templating languages is a testament to the power of templating. Every templating system requires two things:
Subsequently, the conversion specifier syntax was built organically with seemingly randomly chosen syntax characters. One only needs to glance at the man page to see printf's resemblance to an occult magic spell book. Educating a C programmer in disciplined and deliberate programming must start with a solid foundation built on precisely specified systems. printf's syntax does not set an example to follow.
The use of printf works great in simple programs, where the output is almost magically shuttled away to stdout. However, this paradigm breaks down when printf becomes repurposed as a way to create formatted strings. The numerous buffer overflows attributable to sprintf are impossible to count. snprintf is almost no better, as it requires the programmer to estimate an upper bound on how much space the string will take up before printing it.
The bugs caused by sprintf and snprintf do not arise from flaws in C, but rather because the fire and forget semantics of printf are incorrectly applied to strings. Essentially, sprintf and snprintf are hacks to work on character arrays from a system that worked on buffering. Unfortunately, C programmers have been indoctrinated to use the functions because they have been portrayed as the "logical" extensions to printf.
printf is a great way to introduce C programmers to variadic arguments. The ability to push a variable number of arguments to a function is essential to concise code. However, where printf falls short is that this is the ONLY way to marshall data to printf. This shortcoming is most pronounced when outputting all the fields of a struct value. It is necessary that the number of conversion specifiers at least matches the number of fields in the struct. However, requiring the programmer to push all the fields of a struct into variadic arguments makes printf seem cumbersome. Additionally, when the fields encode individual bits with significance, the arguments to printf become especially hairy. Not only is this a waste of code, it is a waste of stack space; ideally, the programmer would only have to specify a POINTER to the struct to print it out.
When learning a new language, the first few concepts or elements encountered shape our understanding of that language. With decades of hindsight, it is necessary to look again at how new C programmers are educated. Personally, I stress new programmers because very few existing C programmers are likely to question their foundations. There is no reason to enumerate all the reasons seasoned C programmers will cling to the old "tried and true" ways. I only have the motiviation to build a new foundation for those willing to learn.