I have a repeated print that prints a bunch of non null-terminated char*
's like so:
int len1, len2, len3, len4;
char *str1, *str2, *str3, *str4;
get_vals(message, val1, &str1, &len1);
get_vals(message, val2, &str2, &len2);
get_vals(message, val3, &str3, &len3);
get_vals(message, val4, &str4, &len4);
printf("%.*s %.*s %.*s %.*s", len1, str1, len2, str2, len3, str3, len4, str4);
where get_vals
sets the char *
pointer to the value in memory and sets the len
to the length of the text. It is possible for the passed in char *
to be set to NULL
, and if so, the length field will be set to 0
. It seems this print has happened many times and not segfaulted, I assume due to the fact that the length specifier is 0
and so there is probably no dereferencing. However, is this always safe? Perhaps it is OS or libc version dependent? Is it worth doing a safety check like so:
printf("%.*s %.*s %.*s %.*s",
len1, str1 ? str1 : "",
len2, str2 ? str2 : "",
len3, str3 ? str3 : "",
len4, str4 ? str4 : "");
CodePudding user response:
When you write printf("%.*s", 0, NULL)
, you are using a s
specifier and you setting the precision to 0
. I looked through the relevant parts of section 7.21.6 of N1570. When documenting the s
specifier, it says:
the argument shall be a pointer to the initial element of an array of character type. Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written.
So, technically, just looking at the first part of that quote, you do need to provide a pointer to an array instead of providing NULL. So your code is not following that part of the standard.
However, you set your precision to 0, so the second part of the quote tells us that the printf
function is not actually going to write any characters from that array to the output. This implies to me that it won't try to read any characters either: reading past the end of the array is unsafe so printf
implementations should not do that. So your code will probably work in practice and it's hard to imagine a case where it would fail. The biggest problem I can think of in practice is that static analyzers or validators might complain about your code.
CodePudding user response:
Is it guaranteed by the C standard to be safe to do
printf("%.*s", 0, NULL)
?
Code also has another problem.
NULL
is a implementation-defined null pointer constant. Its type may be void *
, int
, long
, long long
, unsigned
, and a few other integer types. It is likely to be the same size and ...
parameter compatible with char *
or void*
, but is not specified as such. Passing NULL
to match a "%s"
incurs a risk of undefined behavior.
Better to ask:
Is it guaranteed by the C standard to be safe to do
printf("%.*s", 0, (char *) NULL)
?
In this case, the answer by @David Grayson well applies: Technical undefined behavior, but often OK. [Although I do not abide by "This implies to me that it won't try to read any characters" as library implementations do not need to follow the rules of user code - they cheat.]
For high portability, do not use printf("%.*s", 0, NULL)
for these 2 reasons.
Alternative:
printf("%.*s", 0, "");