76

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, at least 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
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Deepanjan Mazumdar
  • 1,447
  • 3
  • 13
  • 20
  • Neither crashes on VS2010. It just prints `(null)` for the null pointer. – Mysticial Jul 21 '12 at 04:05
  • Yes, correct. The answer might very much be architecture specific. – Deepanjan Mazumdar Jul 21 '12 at 04:08
  • 1
    I didn't know you could `printf` a null pointer in the first place... I was expecting both of them to crash. Now to see whether this is specified, implementation-defined, undefined behavior, or just a compiler bug. – Mysticial Jul 21 '12 at 04:10
  • I added two more tags to help attract the attention of those who could answer this. – Mysticial Jul 21 '12 at 04:15
  • 5
    Undefined behavior means it might crash, it might appear to succeed, it might erase your hard drive, or it might show sunshine and puppies on your screen. You never know. – Adam Rosenfield Jul 21 '12 at 04:20
  • Is there anywhere in the standard that says anything about this? I looked through, but can't find anything saying this is undefined - probably this is unspecified (and thus implementation-defined). – nhahtdh Jul 21 '12 at 04:22
  • 1
    Per 7.1.4: Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined. – R.. GitHub STOP HELPING ICE Jul 21 '12 at 04:26
  • Also, `NULL` is an `int`, not a `const char*`. – aschepler Jul 24 '12 at 21:38
  • For %p: http://stackoverflow.com/questions/10461360/does-printf-null-pointer-with-p-always-give-nil – Ciro Santilli OurBigBook.com Jul 20 '15 at 09:19
  • no, ok, but, nobody gave an workaround ? – Jackt Jun 15 '21 at 22:05

4 Answers4

85

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:

printf("%s\n", NULL);

gcc is (ahem) smart enough to deconstruct this into a call to puts. The first printf, this:

printf("test %s\n", NULL);

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:

movl    $.LC0, %eax
movl    $0, %esi
movq    %rax, %rdi
movl    $0, %eax
call    printf      ; <-- Actually calls printf!

(Comments were added by me.)

But the second one produced this code:

movl    $0, %edi    ; Stores NULL in the puts argument list
call    puts        ; Calls puts

Note that this optimization is correct, i.e. it produces the same result for valid strings; notably puts prints a newline character after the string.

Chris Reuter
  • 1,458
  • 10
  • 8
  • 13
    Good answer (though R. beat you by a few minutes). But there's nothing weird about the newline. `puts()` prints the argument string to stdout followed by a newline. (By contrast, `fputs()` prints to a specified file and *doesn't* add a newline.) – Keith Thompson Jul 21 '12 at 04:59
  • Huh. I didn't know about the newline until you told me. I guess it's because I always just used fputs instead. – Chris Reuter Jul 21 '12 at 20:52
38

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) to puts(x), and puts does not have the silly code to print (null) when it sees a null pointer, whereas common implementations of printf 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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Is this behaviuor calling puts and printf in different scenario part of C standard. Or a compiler can chose its own mechanisms. Since on visual c++ it works fine. – Krishna Oza Feb 15 '14 at 07:22
  • 3
    The compiler can make any transformation that does not change the behavior of a valid program. (Here it changed the behavior of an invalid one.) – R.. GitHub STOP HELPING ICE Feb 15 '14 at 17:42
21

Section 7.1.4 (of C99 or C11) says:

§7.1.4 Use of library functions

¶1 Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.

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):

§7.21.6.1 The fprintf function

s     If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type. [...]

     If an l length modifier is present, the argument shall be a pointer to the initial element of an array of wchar_t type.

p     The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.

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 the p 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).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    *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.* actually it says *the argument shall be a pointer to the initial element of an array of character type* and the standard says the violation of a *shall* that appears outside of a constraint is undefined behavior (C99, 4.p2). – ouah Aug 05 '12 at 11:47
  • @ouah: is what you're saying different from the quoted section of §7.1.4 says (_'null pointer ... behaviour is undefined'_), or what I say it says (_'null pointer ... behaviour is explicitly undefined'_)? – Jonathan Leffler Aug 05 '12 at 13:30
  • I'm adding this as an extra note: that 4.p2 could be also used with the specification of `printf` to show that the call with the `NULL` argument is UB. In my comment the word *actually* is maybe superfluous. – ouah Aug 05 '12 at 13:49
8

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.

Yunchi
  • 5,529
  • 2
  • 17
  • 18