Причина, по которой вы получаете странный вывод, заключается в том, что у вас неопределенное поведение. Функции, обрабатывающие строки (включая печать их с помощью спецификатора формата %s
с printf
), ожидают, что они будут завершены нулем, в противном случае вы получите неопределенное поведение. И если это произойдет, никакой конкретный результат не гарантируется. Он может работать как положено, может выдавать странные результаты, может вызвать сбой программы или сделать что-то другое. Обычно при печати строк он просто продолжает печатать до тех пор, пока не произойдет нарушение доступа, и он не выйдет из строя или пока не встретит следующий нулевой байт (что, вероятно, произойдет раньше, чем позже, и, пока это не произойдет, он напечатает «мусор»).
Вот почему изменение внешне несвязанных вещей может изменить UB для проявления по-разному. Из-за изменений в Примере 2, стек выглядит по-разному, и когда к нему неправильно обращаются из-за UB, он получит доступ к различным вещам. Возможно, что точка, напечатанная в Примере 1 при печати hello
, на самом деле является точкой из char dot = '.';
.
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
. Печать это неопределенное поведение, поскольку она не завершена нулем.
char bye[5] = {'b', 'y', 'e', '\n', '\0'};
Вы можете напечатать это как строку, так как оно завершено нулем.
char end[] = "end";
Поскольку вы сделали это со строковым литералом, терминатор нуля добавляется автоматически;Вы можете распечатать это. char end[]
- это массив четырех символов: 'e'
, 'n'
, 'd'
и '\0'
.
char dot = '.';
Это один char
, а нестрока. Печать с %s
- неопределенное поведение, и ваш компилятор, вероятно, предупредит вас. Вместо этого напечатайте его с помощью %c
.
char oops[] = {'o', 'o', 'p', 's'};
То же, что и первый, нулевой терминатор отсутствует, поэтому печать с помощью %s
является неопределенным поведением.
мусор все еще там! Я чувствую себя хорошо для них, но почему конец был напечатан дважды?
Компиляторы часто помещают все строки рядом друг с другом в двоичном файле программы. Так что может случиться так, что ваша строка end
появится сразу после не завершенного нулем oops
Таким образом, в памяти он выглядит как oopsend\0
, поэтому он может печатать "oopsend"
(или делать что-то совершенно иное, поскольку оно все еще не определено).
В общем, мало что можно извлечь из удивления, почему UBделает то, что делает, потому что он не согласован (он может делать что-то другое каждый раз, когда вы запускаете его на другом компиляторе / машине, или работать, пока вы не продемонстрируете это кому-то другому, а затем внезапно не завершите программу)Просто посмотрите на это как на что-то неправильное, чего не следует делать.