用匯編語言研究C語言的全局變量、局部變量、參數、返回值放在哪里


前提知識

  c0s調用main函數的地址:  11ah

  main函數的連接地址:  01fah

一、全局變量與局部變量

測試程序

int a1,a2,a3;

void f(void);
void g(void);
void h(void);
main()
{
    int b1,b2,b3;
    a1 = 0xa1;a2 = 0xa2;a3 = 0xa3;
    b1 = 0xb1;b2 = 0xb2;b3 = 0xb3;
}

void f(void)
{
    int c1,c2,c3;
    a1 = 0x0fa1;a2 = 0x0fa2; a3 = 0x0fa3;
    c1 = 0xc1; c2 = 0xc2; c3 = 0xc3;
    c1 = c2 + c3;
}

void g(void)
{
    int i = 100;
    while(i--);
}

void h(void)
{
    int h1,h2,h3,h4,h5,h6,h7;
    h1 = 0xc1; h2 = 0xc2; h3 = 0xc3;h4 = 0xc4;
    h5 = 0xc5; h6 = 0xc6; h7 = 0xc7;
    h1 = h2 + h3;
    h2 = h3 + h4;
}
View Code

編譯、連接后,用debug調試這段代碼,根據函數分別貼出對應的反匯編代碼

1、main函數

(1)全局變量

main()
{
    int b1,b2,b3;
    a1 = 0xa1;a2 = 0xa2;a3 = 0xa3;
    b1 = 0xb1;b2 = 0xb2;b3 = 0xb3;
}

對應的反匯編代碼

  可以看到全局變量,a1、a2、a3的地址分別是ds:[01a6]、ds:[01a8]、ds:[01aa]。

  可以看到,ds:[01a6]的物理地址是16266h,而程序的結束位置是CS:[2a0]的物理地址是15d60。可見,全局變量位於代碼段外。ds=ss,而sp=ffe6,ss:sp的物理位置為260a6h,即棧頂位於260a6h,棧應高於棧頂。所以全局變量不可能位於棧區。

    綜上所述,我認為全局變量位於非代碼段,非棧段,而位於data段(初始化)或者bss段(未初始化)。

(2)局部變量

 開辟在棧中的局部變量 

  a) 編譯器先將BP壓入棧

  b)用BP保存棧指針,然后SP-6,為局部變量開辟空間。

    push bp

    mov bp,sp

    sub sp,+6

  c) 函數返回前恢復棧,釋放局部變量空間

    mov sp,bp

  d) 恢復BP

2、f函數

void f(void)
{
    int c1,c2,c3;
    a1 = 0x0fa1;a2 = 0x0fa2; a3 = 0x0fa3;
    c1 = 0xc1; c2 = 0xc2; c3 = 0xc3;
    c1 = c2 + c3;
}

  對應的反匯編代碼

  局部變量c1、c2被開辟在寄存器SI、DI中,而c3則開辟在棧中。

  開辟在寄存器中的局部變量 

  a) 編譯器先將BP壓入棧

  b)用BP保存棧指針,將SI、DI壓入棧,從而為局部變量開辟空間

    push bp

    mov bp,sp

    push si

    push di

  c) 函數返回前恢復寄存器(釋放局部變量),然后恢復棧

    pop di

    pop si

    mov sp,bp

  d) 恢復BP

  無論開辟到棧中,還是開辟在寄存器中,棧指針的移動都是相同的。

3、g函數

void g(void)
{
    int i = 100;
    while(i--);
}

  對應的反匯編代碼

  通過這個函數的反匯編可以重復驗證“開辟在寄存器中的局部變量”的結論。

4、h函數

 還會開辟到其他地方嗎?

void h(void)
{
    int h1,h2,h3,h4,h5,h6,h7;
    h1 = 0xc1; h2 = 0xc2; h3 = 0xc3;h4 = 0xc4;
    h5 = 0xc5; h6 = 0xc6; h7 = 0xc7;
    h1 = h2 + h3;
    h2 = h3 + h4;
}

 對應的反匯編代碼

  可以看到,當有7個局部變量的情況下,局部變量除了開辟在棧中,就是寄存器中,沒別的地方了

 什么時候開辟在棧中,什么時候開辟在寄存器中?這個問題還搞不懂,不過感覺沒多大意義。

二、函數如何傳遞參數,又是如何接受的呢

  測試程序

void showchar(char a,char b);

main()
{
    showchar('a',2);
}

void showchar(char a,char b)
{
    char r;
    r  = a;
    r  = b;
}
View Code

1、main函數

main()
{
    showchar('a',2);
}

對應的反匯編代碼

a) 函數的參數是通過棧傳遞,而且是從右到左依次入棧

b) 即使是char型變量,在傳遞參數時,也是占用兩個字節,因為push操作是兩個字節為單位的。取的時候,是按照它的類型來的

c) 釋放參數可以通過多次pop來實現。事實上,有時是通過“add sp,+數值”來實現的。顯然,后者釋放空間來的更快。

2、showchar函數

void showchar(char a,char b)
{
    char r;
    r  = a;
    r  = b;
}

對應的反匯編代碼

d) 調用函數前后堆棧保持一致,也就是函數返回時要讓堆棧指針恢復到和進入函數時一樣的狀態

e) 函數接受形參是通過從棧中取的

f) 對於為初始化的局部變量,編譯器是不會給它賦初值的,而是拿來就用

g) main函數的局部變量的壽命要比調用函數showchar的形參壽命長

h) BP不僅保存了堆棧的值,而且通過它可以找到傳入參數的值,BP+4是第一個參數,BP+6是第二個參數......取參數是從左到右取的(這就是堆棧的妙處)

三、函數返回值

 測試程序

char f(void);

main()
{
    char c;
    c = f();
}

char f(void)
{
    return 'a';
}
View Code

1、main函數

char f(void);

main()
{
    char c;
    c = f();
}

對應的反匯編代碼

2、f函數

char f(void)
{
    return 'a';
}

對應的反匯編代碼

函數返回值:

  char型 AL

  int型 AX  

四、總結

1、全局變量

  全局變量位於非代碼段,非棧段,而位於data段(初始化)或者bss段(未初始化)。

2、局部變量

a、開辟在堆棧中的局部變量,通過“mov sp,bp”來釋放空間

b、開辟在寄存器中的局部變量(push si/di),通過"pop si/di”來實現

c、未初始化的局部變量,編譯器並不會給它賦初值,而是拿來就用

3、如何傳遞參數(主函數)

a、函數的參數是通過棧傳遞,而且是從右到左依次入棧

b、即使是char型變量,在傳遞參數時,也是占用兩個字節,因為push操作是兩個字節為單位的。

c、showchar('a',2)這樣的傳入兩個常數,也會在堆棧中開辟兩個空間,也即對應兩個實參變量。

4、函數如何接收參數(子函數)

a、 函數接受形參是通過從棧中取的

b、通過BP可以找到傳入參數的值,BP+4是第一個參數,BP+6是第二個參數......取參數是從左到右取的

5、如何釋放參數(主函數)

   釋放參數可以通過多次pop來實現。事實上,有時是通過“add sp,+數值”來實現的。

6、函數返回值

 char型 AL

 int型 AX  

五、其他結論

1、局部變量、傳遞參數和接收參數都與堆棧脫不了干系

  一個結論:局部變量、傳遞參數和接收參數都伴隨着入棧、出棧、讀堆棧中的內容等操作。對堆棧的這些操作,完成了變量和參數的創建、使用和釋放。

  開辟在堆棧中的變量和參數都是在堆棧中開辟空間的,釋放的時候也就通過移動sp或者pop指令來完成。

  開辟在寄存器中的變量,是通過“push si/di”來獲取空間的,釋放的時候是通過“pop si/di”來完成的。

2、C語言函數匯編后的代碼 

push bp
mov bp,sp
...
...
mov sp,bp
pop bp
ret

   這些代碼是每個函數反匯編都會出現的,怎樣理解呢?

a、BP保存了堆棧的值,所以可以利用堆棧來開辟局部變量的空間。(倘若不保存堆棧的值,將來怎樣回收這些空間呢?可能會比較麻煩)

b、通過BP可以找到傳入參數的值,BP+4是第一個參數,BP+6是第二個參數......取參數是從左到右取的(這就是堆棧的妙處) 

 

參考:王爽匯編語言綜合研究-函數如何接收不定數量的參數

   王爽匯編語言綜合研究-使用內存空間

   《匯編語言》319頁研究實驗3 “使用內存空間”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM