首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C 函式參數入棧組合語言深度解析:為何 char 參數位址反而遞減?

     編輯:關於C

現象觀察

同樣的函式 print(a,b,c),參數型別不同,輸出位址順序竟然相反:

  • int 參數:位址 遞增(0x22FF40, 0x22FF44, 0x22FF48)
  • char 參數:位址 遞減(0x22FF2C, 0x22FF28, 0x22FF24)

這違反「參數從右往左壓棧、位址遞增」的直覺?

組合語言揭秘

用 gcc -S test.c 產生 .s 檔,觀察 print1 (int) 與 print2 (char) 的差異。

int 版本:參數直接在呼叫者棧框

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)

參數在 呼叫者 的棧框,位址隨參數順序遞增,符合「右至左壓棧、位址低到高」。

char 版本:被呼叫者複製到區域變數

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),這些區域變數位址自然是 遞減 的(棧向下增長)。

根本原因

  1. 整數提升:char 傳遞時擴展為 int (4 位元組)。
  2. 被呼叫者建立區域副本:為了取得 char* 位址給 printf("%p", &a),編譯器在函式內部配置 char 大小的區域變數並複製值。
  3. 區域變數位址遞減:區域變數配置在 ebp 下方,越晚宣告位址越低。

結論

  • 「參數從右往左壓棧、位址遞增」指的是 呼叫者棧框上的參數區。
  • 若函式內部取參數位址 (¶m),且參數經過型別轉換/擴展,編譯器可能建立區域副本,此時觀測到的是區域變數位址,非原始參數位址。
  • 組合語言不騙人:遇到反直覺現象,看 .s 檔才是正途。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved