Подтвердить что ты не робот

Добавление символа новой строки в printf() изменяет поведение кода

По какой-то причине добавление \n в printf() изменяет поведение ниже кода. Код без \n печатает (null), тогда как код с \n приводит к Segmentation fault.

Printf.c

#include <stdio.h>

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

Printf.c - вывод

$ 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 - Выход

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

Мне любопытно понять причину этого.

4b9b3361

Ответ 1

Оба - это undefined поведение, поэтому ответ может остановиться здесь.

Но есть хотя бы объяснение вывода (null). Это расширение в glibc (библиотека GNU C). Передача 0 для %s в printf() считается undefined в стандарте C и поэтому может очень хорошо привести к сбою. Разработчики glibc решили сделать что-то значимое.

Тем не менее, вторая проблема заключается в том, что с новой строкой компилятор решает оптимизировать: вместо printf("%s\n", argv[1]) он выполняет puts(argv[1]) , который семантически эквивалентен в соответствии со стандартом C, поэтому разрешенная оптимизация. Но glibc "(null) -trick" реализуется только в printf().

В вашей программе есть еще undefined поведение: вы потенциально получаете доступ к argv за пределами. Там нет гарантии того, какое значение вы найдете в argv[i], когда i > argc. Там небольшой шанс argc может быть 0, поэтому вы можете испытать что-нибудь еще.

Ответ 2

В обоих случаях код имеет undefined поведение, если программе не даны аргументы командной строки, поэтому все может случиться.

Поскольку вам любопытно (полезно для вас!), вот потенциальное объяснение того, что вы наблюдаете:

  • printf("%s\n", argv[1]); может быть оптимизирован компилятором в puts(argv[1]);, тогда как printf("%s", argv[1]); все еще вызывает printf().

  • некоторые реализации printf принимают нулевой указатель в качестве аргумента для преобразования %s, следовательно, вывод (null).

  • puts() имеет поведение undefined для нулевого указателя, в вашем случае - ошибка сегментации.

Попробуйте скомпилировать оба без оптимизации (-O0) и посмотреть, есть ли у вас вывод (null) с и без \n.

Вы можете играть с godbolt explorer и посмотреть, как clang изменяет поведение с -O0, но не gcc.

Ответ 3

Выполнение без аргументов argv[1] должно быть указателем NULL. Если argv[1] NULL

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

будет вызывать поведение undefined.