Это неопределенное поведение и не должно приводить к сбою (или любому другому специфическому поведению). Компилятор может создавать любой код, который ему нравится, для таких случаев. Поскольку вы спросили, почему код, созданный clang, не дает сбоя, нам нужно углубиться в этот код. Вот что выдает clang trunk при компиляции с -O3
на x86_64:
main: # @main
pushq %rbp
movq %rsp, %rbp # Build stack frame
movl $.L.str, %edi
movl $14, %esi
xorb %al, %al # no XMM registers used by varargs call
callq printf # printf(%edi = "%d\n", %esi = 14)
movl $.L.str, %edi
xorb %al, %al # no XMM registers used by varargs call
callq printf # printf(%edi = "%d\n", %esi = ?)
xorl %eax, %eax
popq %rbp
ret # return %eax = 0
Поскольку p
неинициализирован, Clang сегодня решил скомпилировать выражение *p
вообще ни к чему. Это законное преобразование, потому что Clang может доказать, что выражение имеет неопределенное поведение. В этом случае печатаемое значение соответствует тому, что попадает в регистр %esi
во время вызова printf
(на моем аппарате это -1
). Возможно, это не то, что вы ожидали, но это характер неопределенного поведения!