The explanation is that even if those codes are practically the same for you, the resulting binary can be very different ... it is even highly probable that the memory positions that it gives change depending on whether you compile with or without optimizations
And what is the difference?
In your case the difference is in the variable i
. I do not know what compiler and compilation options you use, as I say the result is dependent on the compiler and its options ... in fact, if we compare the codes 2 and 3 that are the simplest we can see many differences
Linux - Clang 6.0.0 - options: no optimizations
Code 2
main: # @main
push rbp
mov rbp, rsp
sub rsp, 48
movabs rdi, offset .L.str
lea rsi, [rbp - 32]
mov dword ptr [rbp - 4], 0
mov al, 0
call printf
movabs rdi, offset .L.str
lea rsi, [rbp - 32]
add rsi, 4
mov dword ptr [rbp - 36], eax # 4-byte Spill
mov al, 0
call printf
movabs rdi, offset .L.str
lea rsi, [rbp - 32]
add rsi, 8
mov dword ptr [rbp - 40], eax # 4-byte Spill
mov al, 0
call printf
movabs rdi, offset .L.str
lea rsi, [rbp - 32]
add rsi, 12
mov dword ptr [rbp - 44], eax # 4-byte Spill
mov al, 0
call printf
xor ecx, ecx
mov dword ptr [rbp - 48], eax # 4-byte Spill
mov eax, ecx
add rsp, 48
pop rbp
ret
.L.str:
.asciz "%p\n"
Code 3
main: # @main
push rbp
mov rbp, rsp
sub rsp, 48
mov dword ptr [rbp - 4], 0
mov dword ptr [rbp - 36], 0
.LBB0_1: # =>This Inner Loop Header: Depth=1
cmp dword ptr [rbp - 36], 4
jge .LBB0_4
movabs rdi, offset .L.str
lea rax, [rbp - 32]
movsxd rcx, dword ptr [rbp - 36]
shl rcx, 2
add rax, rcx
mov edx, dword ptr [rbp - 36]
mov rsi, rax
mov al, 0
call printf
mov dword ptr [rbp - 40], eax # 4-byte Spill
mov eax, dword ptr [rbp - 36]
add eax, 1
mov dword ptr [rbp - 36], eax
jmp .LBB0_1
.LBB0_4:
xor eax, eax
add rsp, 48
pop rbp
ret
.L.str:
.asciz "%p arreglo[%d]\n"
Linux - Clang 6.0.0 - options: -O3
Code 2
main: # @main
sub rsp, 24
mov rsi, rsp
mov edi, offset .L.str
xor eax, eax
call printf
lea rsi, [rsp + 4]
mov edi, offset .L.str
xor eax, eax
call printf
lea rsi, [rsp + 8]
mov edi, offset .L.str
xor eax, eax
call printf
lea rsi, [rsp + 12]
mov edi, offset .L.str
xor eax, eax
call printf
xor eax, eax
add rsp, 24
ret
.L.str:
.asciz "%p\n"
Code 3
main: # @main
sub rsp, 24
mov rsi, rsp
mov edi, offset .L.str
xor edx, edx
xor eax, eax
call printf
lea rsi, [rsp + 4]
mov edi, offset .L.str
mov edx, 1
xor eax, eax
call printf
lea rsi, [rsp + 8]
mov edi, offset .L.str
mov edx, 2
xor eax, eax
call printf
lea rsi, [rsp + 12]
mov edi, offset .L.str
mov edx, 3
xor eax, eax
call printf
xor eax, eax
add rsp, 24
ret
.L.str:
.asciz "%p arreglo[%d]\n"
Notice that once the optimizations have been applied, the two binaries are practically the same ... whereas without optimizations they seem to be very little ... and different results will be obtained when changing the compiler ... for example, if we compile the two examples scon gcc 4.4.7
we get:
without optimizations
Code 2
.LC0:
.string "%p\n"
main:
push rbp
mov rbp, rsp
sub rsp, 16
lea rax, [rbp-16]
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
lea rax, [rbp-16]
add rax, 4
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
lea rax, [rbp-16]
add rax, 8
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
lea rax, [rbp-16]
add rax, 12
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
Code 3
.LC0:
.string "%p arreglo[%d]\n"
main:
push rbp
mov rbp, rsp
sub rsp, 32
mov DWORD PTR [rbp-4], 0
jmp .L2
.L3:
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-32]
movsx rdx, edx
sal rdx, 2
lea rcx, [rax+rdx]
mov eax, DWORD PTR [rbp-4]
mov edx, eax
mov rsi, rcx
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
add DWORD PTR [rbp-4], 1
.L2:
cmp DWORD PTR [rbp-4], 3
setle al
test al, al
jne .L3
mov eax, 0
leave
ret
with optimizations
Code 2
.LC0:
.string "%p\n"
main:
push rbx
mov edi, OFFSET FLAT:.LC0
xor eax, eax
sub rsp, 16
mov rsi, rsp
call printf
lea rsi, [rsp+4]
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
lea rsi, [rsp+8]
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
lea rsi, [rsp+12]
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 16
pop rbx
ret
Code 3
.LC0:
.string "%p arreglo[%d]\n"
main:
push rbx
xor edx, edx
mov edi, OFFSET FLAT:.LC0
xor eax, eax
sub rsp, 16
mov rsi, rsp
call printf
lea rsi, [rsp+4]
mov edx, 1
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
lea rsi, [rsp+8]
mov edx, 2
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
lea rsi, [rsp+12]
mov edx, 3
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 16
pop rbx
ret
As you see, they seem rather little ... roughly if they have some details in common ... but the same are not equal.
Do not worry about those details since the exact position of an element seldom matters ... what's really important is that your pointers point to where they should ... the memory positions are random and that randomness grows as you increase the complexity of the program.