Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it guaranteed by the C standard to be safe to do printf("%.*s", 0, NULL)?

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 : "");
like image 715
someone serious Avatar asked Dec 27 '21 19:12

someone serious


People also ask

Can you print null in C?

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.

How to printf pointer in C?

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.

What does NULL print mean?

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.

What is the size of NULL pointer?

NULL in C is defined as (void*)0. Since it's a pointer, it takes 4 bytes to store it.


2 Answers

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.

like image 146
David Grayson Avatar answered Sep 22 '22 19:09

David Grayson


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, "");
like image 26
chux - Reinstate Monica Avatar answered Sep 23 '22 19:09

chux - Reinstate Monica