4 ms·
As with so many categorical guidelines, there are circumstances to use it and circumstances to not. The key insight is that printf() is a heavyweight operation
by shadowgovt 2y ago
As with so many categorical guidelines, there are circumstances to use it and circumstances to not.
The key insight is that printf() is a heavyweight operation ("What, you want to build a string? A human readable string? Okay, one second, lemme just pull in the locale library..."). If you're debugging something at the business-logic layer, it's probably fine.
If you're debugging a memory leak, calling a function that's going to make a deep-dive on the stack and move a lot of memory around is likely to obscure your bug.
- mark_undoio 2y ago> If you're debugging a memory leak, calling a function that's going to make a deep-dive on the stack and move a lot of memory around is likely to obscure your bug. shudder memories of my early days in kernel-level programming where using a printf used to just "fix" some bugs. That came down to an uninitialised variable (which calling printf was helpfully initialising by using the stack). As a result of that early experience, when I'm in the headspace of very low-level bugs, I find it helps to think of printf both as "this will tell me some variable values" and as a source of other clues: if it makes a weird value disappear or change then you might have uninitialised data on your stack, if it makes a flaky behaviour become stable (either disappear or become repeatable) then it's probably a race condition, etc. A reference to James Mickens' "The Night Watch" feels appropriate here: https://www.usenix.org/system/files/1311_05-08_mickens.pdf https://www.usenix.org/system/files/1311_05-08_mickens.pdf
- fweimer 2y agoIsn't localization triggered for specific format strings only? There are many corner cases when typical printf implementations call malloc, but it's usually not too hard to avoid them. If you are worried about including <stdio.h> and potential side effects from that, you can use __builtin_printf instead.
- shadowgovt 2y agoYou're correct, but then you're adding the additional burden to your debugging process of memorizing which format strings trigger localization. I'd have to look it up, but I'm pretty sure numbers do, which means anytime you %d or %f you're inviting the devil in. When I say move memory around in this context, I mean do a lot of stack operations. You can leak from the stack too (drop a pointer from the stack without freeing the underlying heap memory it referenced), and it's harder to catch that if printf has come along and completely rewritten your unused stack memory as consequence of reporting on the state of your program. That having been said, the point is that context matters and what you're debugging matters for the question of what tool to use. If you're operating in an interpreted language, you can probably trust that The interpreter is making it difficult to leak memory like that. On the other hand, interpreters have bugs too, and using a language that is interpreted instead of compiled machine code makes bugs in the execution layer unlikely, but not impossible...
- fweimer 2y agoAdmittedly, I forgot about %f/%g and the decimal separator. I think the rest is fairly obscure (such as %lc for wide character to multibyte character translation). For integers you need %Id (variant digits) or %'d (grouping). The decimal separator is a bit of a problem for JSON generation, too. Some systems have snprintf_l, but it's not very widespread.