23

For some reason, adding \n to printf() changes the behaviour of below code. The code without \n prints (null) whereas the code with \n leads to Segmentation fault.

Printf.c

#include <stdio.h>

int main(int argc, char* argv[]){
    printf("%s", argv[1]);
} 

Printf.c - Output

$ gcc -o Printf Printf.c
$ ./Printf
(null)

Printf_Newline.c

#include <stdio.h>

int main(int argc, char* argv[]){
    printf("%s\n", argv[1]);
}

Printf_Newline.c - Output

$ gcc -o Printf_Newline Printf_Newline.c
$ ./Printf_Newline
Segmentation fault (core dumped)

I am curious to understand the reason behind this.

14
  • 44
    Undefined behaviour is undefined. Commented Aug 17, 2017 at 14:48
  • 4
    Who knows what will happen when your code has a bug in it that causes undefined behaviour. Maybe next time you run it, the world will end - best to fix the bugs :) Commented Aug 17, 2017 at 14:48
  • 1
    @ChrisTurner You think that it's indeed undefined behavior to pass a NULL pointer for a "%s" specifier, that's strictly true but why then did it print (null) in the other case if there is no such string passed to printf()? I think this might be an actual bug somewhere. Commented Aug 17, 2017 at 14:50
  • 2
    @IharobAlAsimi: "why then did it print (null)" because the GNU guys were creative, had free beers, too much time ... whatever. Commented Aug 17, 2017 at 14:51
  • 7
    @ChrisTurner argv[argc] is always a null pointer. Commented Aug 17, 2017 at 17:40

3 Answers 3

55

Both are undefined behavior, so an answer could stop right here.

But there's at least an explanation for the output of (null). This is an extension in glibc (the GNU C library). Passing 0 for %s in printf() is considered undefined in the C standard and therefore could very well result in a crash. The developers of glibc decided to do something meaningful instead.

The reason the second crashes nevertheless is that with the newline, the compiler decides to optimize: Instead of printf("%s\n", argv[1]), it executes puts(argv[1]), which is semantically equivalent according to the C standard, therefore an allowed optimization. But glibcs "(null)-trick" is only implemented in printf().

There's another undefined behavior in your program: You potentially access argv out of bounds. There's no guarantee what value you will find at argv[i] when i > argc. There's a slight chance argc could be 0, so you could experience anything else as well.

Sign up to request clarification or add additional context in comments.

10 Comments

Where did you get this glibc thing? o__O
It's undefined behaviour only if i > argc (strictly greater, not greater-or-equal) - argv[argc] is guaranteed to be a null pointer. But I don't see any check of argc >= 1 in that program.
"decides to optimize": Nice spot: printf("%d, %s\n", argc, argv[1]); doesn't crash for argc being equal to 1, but prints: 1, (null)<newline> :-)
@alk Perhaps if you call execvp with argv[0] set to NULL.
@alk would have to test that, but maybe i can provoke it with char *argv[] = { 0 }; execv("foo", argv);
|
11

The code has undefined behavior in both cases if the program is not given any command line arguments, so anything can happen.

Since you are curious (good for you!), here is a potential explanation for what you observe:

  • printf("%s\n", argv[1]); can be optimized by the compiler into puts(argv[1]); whereas printf("%s", argv[1]); still invokes printf().

  • some implementations of printf accept a null pointer as an argument for the %s conversion, hence the output (null).

  • puts() has undefined behavior for a null pointer, in your case a segmentation fault.

Try compiling both without any optimization (-O0) and see if you get (null) output with and without the \n.

You can play with godbolt's compiler explorer and see how clang changes behavior with -O0, but not gcc.

2 Comments

Yes, --O0 flag didn't make any change to the behaviour.
@Beginner: it actually depends on your compiler, recent versions of gcc seem to optimize the printf even when told not to with -O0, whereas clang does keep the call to printf().
2

Executing with no arguments argv[1] shall be NULL pointer. With argv[1] being NULL

printf("%s", argv[1]);

will invoke undefined behavior.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.