views:

153

answers:

3

How will various functions that take printf format string behave upon encountering the %c format given value of \0/NULL? How should they behave? Is it safe? Is it defined? Is it compiler-specific?

e.g. sprintf() - will it crop the result string at the NULL? What length will it return?

Will printf() output the whole format string or just up to the new NULL?

Will va_args + vsprintf/vprintf be affected somehow? If so, how?

Do I risk memory leaks or other problems if I e.g. shoot this NULL at a point in std::string.c_str()?

What are the best ways to avoid this caveat (sanitize input?)

+6  A: 

Any function that takes a standard C string will stop at the first null, no matter how it got there.

When you use the %c in a format and use 0 for the character value, it will insert a null into the output. printf will output the null as part of the output. sprintf will also insert the null into the result string, but the string will appear to end at that point when you pass the output to another function.

A std::string will happily contain a null anywhere within its contents, but when you take the c_str method to pass it to a function see the above answer.

Mark Ransom
Despite this being flagged as edited by me, I have in fact simply reverted it to the original, after accidentally editing it instead of my own entry. Sorry. (and I get a "clean-up" badge for my own incompetence!?)
Clifford
+3  A: 

What happens when you output a NUL depends on the output device.

It is a non printing character, i.e. isprint('\0') == 0; so when output to a display device, it has no visible affect. If redirected to a file however (or if calling fprintf()), it will insert a NUL (zero byte) into the file; the meaning of that will depend on how the file is used.

When output to a C string, it will be interpreted as a string terminator by standard string handling functions, although any other subsequent format specifiers will still result in data being placed in the buffer after the NUL, which will be invisible to standard string handling functions. This may still be useful if ultimately the array is not to be interpreted as a C string.

Do I risk memory leaks or other problems if I e.g. shoot this NULL at a point in std::string.c_str()?

It is entirely unclear what you mean by that, but if you are suggesting using the pointer returned by std::string.c_str() as the buffer for sprintf(); don't! c_str() returns a const char*, modifying the string through such a pointer is undefined. That however is a different problem, and not at all related to inserting a NUL into a string.

What are the best ways to avoid this caveat (sanitize input?)

I am struggling to think of a circumstance where you could "accidentally" write such code, so why would you need to guard against it!? Do you have a particular circumstance in mind? Even though I find it implausible, and probably unnecessary, what is so hard about:

if( c != 0 )
{
    printf( "%c", c ) ;
}

or perhaps more usefully (since there are other characters you might want to avoid in the output)

if( isgraph(c) || isspace(c) )
{
    printf( "%c", c ) ;
}

which will output only visible characters and whitespace (space, '\t','\f','\v','\n','\r').

Clifford
+1  A: 

printf() and sprintf() will continue past a '\0' character inserted with %c, because their output is defined in terms of the content of the format string, and %c does not denote the end of the format string.

This includes their count; thus:

sprintf(x, "A%cB", '\0')

must always return 3 (although strlen(x) afterwards would return 1).

caf