Why can I get "1 2"?
For the same reason as the source code, the optimizer knows *p invalid and works according to this assumption. Why is this invalid? Because...
J.2 Undefined Behavior
1 Undefined behavior in the following cases:
A pointer value is used that refers to the space freed up by calling the free or realloc function (7.20.3).
*p not valid. The optimizer knows that *(int*)ap really *p , so it is also invalid.
Looking at IR for both the original and your code with clang -S -O3 -emit-llvm , they are almost exactly the same. Both hardcode 1 and 2 in printf .
%7 = tail call i32 (i8*, ...) @printf(i8* nonnull getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0), i32 1, i32 2)
Here is your main in IR mode.
define i32 @main() #0 { %1 = tail call i8* @malloc(i64 4) %2 = tail call i8* @realloc(i8* %1, i64 4) %3 = bitcast i8* %2 to i32* %4 = bitcast i8* %1 to i32* store i32 1, i32* %4, align 4, !tbaa !2 store i32 2, i32* %3, align 4, !tbaa !2 %5 = icmp eq i8* %1, %2 br i1 %5, label %6, label %8 ; <label>:6 ; preds = %0 %7 = tail call i32 (i8*, ...) @printf(i8* nonnull getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0), i32 1, i32 2) br label %8 ; <label>:8 ; preds = %6, %0 ret i32 0 }
There is almost the same thing, except for the difference in the ordering of tail calls and bit-bits. This is yours.
%1 = tail call i8* @malloc(i64 4) %2 = tail call i8* @realloc(i8* %1, i64 4) %3 = bitcast i8* %2 to i32* %4 = bitcast i8* %1 to i32*
It's theirs.
%1 = tail call i8* @malloc(i64 4) %2 = bitcast i8* %1 to i32* %3 = tail call i8* @realloc(i8* %1, i64 4) %4 = bitcast i8* %3 to i32*
Everything else is the same.
As mentioned in the conversation, if is a red herring. This is just to illustrate how obviously absurd behavior is. He is still there in IR mode.
%5 = icmp eq i8* %1, %2 br i1 %5, label %6, label %8
You can see the optimizer at work if you print p , q and (int*)ap . All of them are the result of the first malloc.
%1 = tail call i8* @malloc(i64 4) ... %8 = tail call i32 (i8*, ...) @printf(i8* nonnull getelementptr inbounds ([10 x i8], [10 x i8]* @.str.1, i64 0, i64 0), i8* %1, i8* %1, i8* %1)
Pointer in order. This is acting out that problem.