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 : "");
The print expects a '\0' terminated array of characters (or string literal) whereas it receives a null pointer. Passing NULL to printf is undefined behavior.
You can print a pointer value using printf with the %p format specifier. To do so, you should convert the pointer to type void * first using a cast (see below for void * pointers), although on machines that don't have different representations for different pointer types, this may not be necessary.
The NULL pointer doesn't point to any address, and attempting to print it causes undefined behavior. Undefined meaning it's up to your compiler or C library to decide what to do when it tries to print NULL. Follow this answer to receive notifications.
NULL in C is defined as (void*)0. Since it's a pointer, it takes 4 bytes to store it.
When you write printf("%.*s", len1, str1)
, where len1
is zero and str1
is a null pointer, you are using a s
specifier and 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 a null pointer. 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.
Is it guaranteed by the C standard to be safe to do
printf("%.*s", 0, NULL)
?
No, not quite.
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, "");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With