Came across an interesting interview question:
test 1:
printf("test %s\n", NULL);
printf("test %s\n", NULL);
prints:
test (null)
test (null)
test 2:
printf("%s\n", NULL);
printf("%s\n", NULL);
prints
Segmentation fault (core dumped)
Though this might run fine on some systems, atleast mine is throwing a segmentation fault. What would be the best explanation of this behavior? Above code is in C.
Following is my gcc info:
deep@deep:~$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
As far as the C language is concerned, the reason is that you're invoking undefined behavior and anything can happen.
As for the mechanics of why this is happening, modern gcc optimizes
printf("%s\n", x)
toputs(x)
, andputs
does not have the silly code to print(null)
when it sees a null pointer, whereas common implementations ofprintf
have this special case. Since gcc can't optimize (in general) non-trivial format strings like this,printf
actually gets called when the format string has other text present in it.First things first:
printf
is expecting a valid (i.e. non-NULL) pointer for its %s argument so passing it a NULL is officially undefined. It may print "(null)" or it may delete all files on your hard drive--either is correct behavior as far as ANSI is concerned (at least, that's what Harbison and Steele tells me.)That being said, yeah, this is really wierd behavior. It turns out that what's happening is that when you do a simple
printf
like this:gcc is (ahem) smart enough to deconstruct this into a call to
puts
. The firstprintf
, this:is complicated enough that gcc will instead emit a call to real
printf
.(Notice that gcc emits warnings about your invalid
printf
argument when you compile. That's because it long ago developed the ability to parse*printf
format strings.)You can see this yourself by compiling with the
-save-temps
option and then looking through the resulting.s
file.When I compiled the first example, I got:
(Comments were added by me.)
But the second one produced this code:
The wierd thing is that it doesn't print the following newline. It's as though it's figured out that this is going to cause a segfault so it doesn't bother. (Which it has--it warned me when I compiled it.)
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.Section 7.1.4 (of C99 or C11) says:
Since the specification of
printf()
says nothing about what happens when you pass a null pointer to it for the%s
specifier, the behaviour is explicitly undefined. (Note that passing a null pointer to be printed by the%p
specifier is not undefined behaviour.)Here is the 'chapter and verse' for the
fprintf()
family behaviour (C2011 — it is a different section number in C1999):The specifications for the
s
conversion specifier preclude the possibility that a null pointer is valid since the null pointer does not point to initial element of an array of the appropriate type. The specification for thep
conversion specifier does not require the void pointer to point at anything in particular and NULL is therefore valid.The fact that many implementations print a string such as
(null)
when passed a null pointer is a kindness that is dangerous to rely upon. The beauty of undefined behaviour is that such a response is permitted, but it is not required. Similarly, a crash is permitted, but not required (more's the pity – people get bitten if they work on a forgiving system and then port to other less forgiving systems).