Добавление символа новой строки в 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)
Мне любопытно понять причину этого.
Ответы
Ответ 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.