同樣的函式 print(a,b,c),參數型別不同,輸出位址順序竟然相反:
int 參數:位址 遞增(0x22FF40, 0x22FF44, 0x22FF48)char 參數:位址 遞減(0x22FF2C, 0x22FF28, 0x22FF24)這違反「參數從右往左壓棧、位址遞增」的直覺?
用 gcc -S test.c 產生 .s 檔,觀察 print1 (int) 與 print2 (char) 的差異。
main:
subl $20, %esp # 預留空間
movl $3, 8(%esp) # 第 3 參數 (最右)
movl $2, 4(%esp) # 第 2 參數
movl $1, (%esp) # 第 1 參數 (最左)
call print1 # 參數位址:%esp, %esp+4, %esp+8 遞增
print1:
pushl %ebp
movl %esp, %ebp
subl $8, %esp # 區域變數空間
leal 8(%ebp), %eax # 取第 1 參數位址 (ebp+8)
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
leal 12(%ebp), %eax # 第 2 參數 (ebp+12)
...
leal 16(%ebp), %eax # 第 3 參數 (ebp+16)
參數在 呼叫者 的棧框,位址隨參數順序遞增,符合「右至左壓棧、位址低到高」。
print2:
pushl %ebp
movl %esp, %ebp
subl $24, %esp # 大量區域空間
movl 8(%ebp), %eax # 取參數值 (非位址)
movl 12(%ebp), %edx
movl 16(%ebp), %ecx
movb %al, -4(%ebp) # 存入區域變數 (ebp-4)
movb %dl, -8(%ebp) # (ebp-8)
movb %cl, -12(%ebp) # (ebp-12)
leal -4(%ebp), %eax # 取區域變數位址傳給 printf
...
leal -8(%ebp), %eax
...
leal -12(%ebp), %eax
關鍵差異:編譯器將 char 參數擴展為 int 傳遞(整數提升),進入函式後,又將低位元組 複製到被呼叫者的區域變數(ebp-4, ebp-8, ebp-12),這些區域變數位址自然是 遞減 的(棧向下增長)。
char 傳遞時擴展為 int (4 位元組)。char* 位址給 printf("%p", &a),編譯器在函式內部配置 char 大小的區域變數並複製值。ebp 下方,越晚宣告位址越低。¶m),且參數經過型別轉換/擴展,編譯器可能建立區域副本,此時觀測到的是區域變數位址,非原始參數位址。.s 檔才是正途。