久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C語(yǔ)言的那些小秘密之堆棧

 qdhtxxlhz 2011-08-01

C語(yǔ)言的那些小秘密之堆棧

分類: 【C語(yǔ)言的那些小秘密】 397人閱讀 評(píng)論(2) 收藏 舉報(bào)

在講解堆棧之前,我們先要來(lái)說(shuō)說(shuō)其實(shí)我們常說(shuō)的堆棧是兩種數(shù)據(jù)結(jié)構(gòu),。那么什么是堆什么又是棧呢?

       棧,,是硬件。主要作用表現(xiàn)為一種數(shù)據(jù)結(jié)構(gòu),是只能在某一端插入和刪除的特殊線性表,。它按照后進(jìn)先出的原則存儲(chǔ)數(shù)據(jù),先進(jìn)入的數(shù)據(jù)被壓入棧底,,最后的數(shù)據(jù)在棧頂,,需要讀數(shù)據(jù)的時(shí)候從棧頂開(kāi)始彈出數(shù)據(jù)(最后一個(gè)數(shù)據(jù)被第一個(gè)讀出來(lái))。棧是允許在同一端進(jìn)行插入和刪除操作的特殊線性表,。允許進(jìn)行插入和刪除操作的一端稱為棧頂(top),,另一端為棧底(bottom);棧底固定,,而棧頂浮動(dòng),;棧中元素個(gè)數(shù)為零時(shí)稱為空棧。插入一般稱為進(jìn)棧(PUSH),,刪除則稱為退棧(POP),。 棧也稱為先進(jìn)后出表。??梢杂脕?lái)在函數(shù)調(diào)用的時(shí)候存儲(chǔ)斷點(diǎn),,做遞歸時(shí)要用到棧!

  以上定義是在經(jīng)典計(jì)算機(jī)科學(xué)中的解釋,。
  在計(jì)算機(jī)系統(tǒng)中,,棧則是一個(gè)具有以上屬性的動(dòng)態(tài)內(nèi)存區(qū)域。程序可以將數(shù)據(jù)壓入棧中,,也可以將數(shù)據(jù)從棧頂彈出,。在i386機(jī)器中,棧頂由稱為esp的寄存器進(jìn)行定位,。壓棧的操作使得棧頂?shù)牡刂窚p小,,彈出的操作使得棧頂?shù)牡刂吩龃蟆?
  棧在程序的運(yùn)行中有著舉足輕重的作用。最重要的是棧保存了一個(gè)函數(shù)調(diào)用時(shí)所需要的維護(hù)信息,,這常常稱之為堆棧幀或者活動(dòng)記錄,。堆棧幀一般包含如下幾方面的信息:
  1. 函數(shù)的返回地址和參數(shù)

  2. 臨時(shí)變量:包括函數(shù)的非靜態(tài)局部變量以及編譯器自動(dòng)生成的其他臨時(shí)變量。

        堆,,是一種動(dòng)態(tài)存儲(chǔ)結(jié)構(gòu),,實(shí)際上就是數(shù)據(jù)段中的自由存儲(chǔ)區(qū),它是C語(yǔ)言中使用的一種名稱,,常常用于動(dòng)態(tài)數(shù)據(jù)的存儲(chǔ)分配,。堆中存入一數(shù)據(jù),總是以2字節(jié)的整數(shù)倍進(jìn)行分配,,地址向增加方向變動(dòng),。堆可以不斷進(jìn)行分配直到?jīng)]有堆空間為止,,也可以隨時(shí)進(jìn)行釋放、再分配,,不存在次序問(wèn)題,。
         所謂動(dòng)態(tài)數(shù)組是指在程序運(yùn)行期間確定其大小的,如常用到的動(dòng)態(tài)數(shù)組,,它們是在程序執(zhí)行過(guò)程中動(dòng)態(tài)進(jìn)行變化的,,即在程序開(kāi)始部分沒(méi)有說(shuō)明大小,只有在程序運(yùn)行期  間用堆的分配函數(shù)為其分配存儲(chǔ)空間,,分配的大小可根據(jù)需要而定,,這些數(shù)據(jù)使用過(guò)后,可釋放它們占用的堆空間,,并可進(jìn)行再分配,。
        堆和棧在使用時(shí)相向生長(zhǎng),,棧向上生長(zhǎng),,即向小地址方向生長(zhǎng),而堆向下增長(zhǎng),,即向大地址方向,,其間剩余部分是自由空間。使用過(guò)程中要防止增長(zhǎng)過(guò)度而導(dǎo)致覆蓋,。
一般的程序我們都是使用小內(nèi)存模式,。

        明白了堆和棧的概念之后我們來(lái)看一個(gè)面試的c語(yǔ)言題目。代碼要相關(guān)要求如下所示:

#include <iostream>
using namespace std;
void print()
{
    //這里進(jìn)行打印arr數(shù)組,,print不準(zhǔn)傳參數(shù)

}
int main()

    int s=0;
    int ss=0;
    char *str="fdsafdsafdsafdsafdsafdsafdsa";
    char fdsa='f';
    char srt[8];
    int arr[]={32,43,3,567,987,21,56};//數(shù)值隨即

    print();
    return 0;
}

剛剛一開(kāi)始看到這個(gè)題目時(shí),,你可能有點(diǎn)發(fā)懵,心想可能在不傳遞參數(shù)的情況下打印arr數(shù)組的內(nèi)容,,但是看看我們的標(biāo)題就應(yīng)該知道該題跟棧有關(guān)系,,在做之前我們先來(lái)回顧幾個(gè)知識(shí)點(diǎn)。

push操作先修改指針,,后將信息入棧,。

ESP為堆棧指針,棧頂有ESP寄存器來(lái)定位,。壓棧的操作使得棧頂?shù)牡刂窚p小,,彈出的操作使得棧頂?shù)牡刂吩龃蟆?/p>

EBP是32位的BP,EBP是基址指針,,EBP與BP的關(guān)系就像AX與AL,、AH的關(guān)系一樣。BP為基指針寄存器,,用它課直接存取堆棧中的數(shù)據(jù),,他的作用在調(diào)用函數(shù)時(shí)保存ESP,,使函數(shù)結(jié)束時(shí)可以正確返回。

c的默認(rèn)函數(shù)壓棧操作為:
參數(shù)是從右向左壓棧的,,默認(rèn)四字節(jié)對(duì)齊,,函數(shù)里面定義的變量是默認(rèn)對(duì)齊方式----變量首地址是自身結(jié)構(gòu)體里邊最大標(biāo)準(zhǔn)數(shù)據(jù)類型字節(jié)的整數(shù)倍。

我們先來(lái)看看上面這段代碼的匯編語(yǔ)句:

//*******************************************start*********************************************//

 .file "push.c"
 .text
.globl print
 .type print, @function
print:
 pushl %ebp
 movl %esp, %ebp
 popl %ebp
 ret
 .size print, .-print
 .section .rodata
.LC0:
 .string "fdsafdsafdsafdsafdsafdsafdsa"
 .text
.globl main
 .type main, @function
main:
 pushl %ebp
 movl %esp, %ebp
 andl $-16, %esp
 subl $64, %esp
 movl %gs:20, %eax
 movl %eax, 60(%esp)
 xorl %eax, %eax
 movl $0, 44(%esp)
 movl $0, 40(%esp)
 movl $.LC0, 36(%esp)
 movb $102, 51(%esp)
 movl $32, 8(%esp)
 movl $43, 12(%esp)
 movl $3, 16(%esp)
 movl $567, 20(%esp)
 movl $987, 24(%esp)
 movl $21, 28(%esp)
 movl $56, 32(%esp)
 call print
 movl $0, %eax
 movl 60(%esp), %edx
 xorl %gs:20, %edx
 je .L3
 call __stack_chk_fail
.L3:
 leave
 ret
 .size main, .-main
 .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
 .section .note.GNU-stack,"",@progbits

//*******************************************end*********************************************//

看看上面的匯編代碼,,我們的重點(diǎn)放在紅色字體部分,,在函數(shù)的開(kāi)頭部分都有這么兩行代碼:

 pushl %ebp               --------------------------->保存上一個(gè)函數(shù)的棧底
 movl %esp, %ebp    --------------------------->用來(lái)保存當(dāng)前堆棧指針的值
ebp存放當(dāng)前函數(shù)棧低的地址,就是說(shuō)ebp可以看做一個(gè)指針,,指向棧頂,,而其實(shí)棧頂存放的數(shù)據(jù)就是上一個(gè)函數(shù)的ebp的值,即就是main函數(shù)的棧底,。

明白了上面的內(nèi)容,,那么我們就可以實(shí)現(xiàn)題目的要求了。代碼如下所示:

#include <iostream>
using namespace std;
void print()
{
    //這里進(jìn)行排序,,sort不準(zhǔn)傳參數(shù)

   unsigned int _ebp;
    __asm{
        mov _ebp,ebp
    }
    int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);
    for(int i=0;i<7;i++)
        cout<<p[i]<<endl;

}
int main()

 int s=0;
 int ss=0;
 char *str="fdsafdsafdsafdsafdsafdsafdsa";
 char fdsa='f';
 char srt[8];
    int arr[]={32,43,3,567,987,21,56};//數(shù)值隨即

    print();
    return 0;
}

其中用紅色標(biāo)記的部分是一個(gè)重點(diǎn),,用匯編語(yǔ)句mov _ebp,ebp;來(lái)獲得ebp寄存器的值,,存放在_ebp中,,ebp存放當(dāng)前函數(shù)棧底的地址,就是說(shuō)ebp可以看做一個(gè)指針,,指向棧頂,,因?yàn)?span style="COLOR: #ff0000"> pushl %ebp,所以棧頂存放的數(shù)據(jù)就是上一個(gè)函數(shù)的ebp的值,,即就是main函數(shù)的棧底,。由此可知*(int*)_ebp意思就是取出棧頂?shù)臄?shù)據(jù),即main函數(shù)的棧底,。棧中數(shù)據(jù)的存儲(chǔ)方式是根據(jù)聲明的先后順序來(lái)的,,所以了為了能夠?qū)rr數(shù)組進(jìn)行打印,我們要計(jì)算出數(shù)組首地址的存儲(chǔ)地址,,由于變量的壓棧方式默認(rèn)是四字節(jié)對(duì)齊,,所以我們使用一句int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);來(lái)得到數(shù)組的首地址,有人可能很疑惑了,,為什么這句 char *str="fdsafdsafdsafdsafdsafdsafdsa";聲明的變量只是占了四字節(jié)呢,?!這里要注意了,,因?yàn)橹羔樧兞康牡刂吩?2位的計(jì)算機(jī)中占用四個(gè)字節(jié),,他的內(nèi)容并不存儲(chǔ)在棧中,而是在堆中,棧中僅僅是保存了指向堆的指針,。講到這兒基本上都豁然開(kāi)朗了吧,。看看下面的運(yùn)行結(jié)果吧,!


為了強(qiáng)調(diào)默認(rèn)的字節(jié)對(duì)齊概念,,我們?cè)賮?lái)修改下代碼得到的運(yùn)行結(jié)果可以上面得做一個(gè)比較。

代碼如下,,紅色部分為修改代碼,。

#include <iostream>
using namespace std;
void print()
{
    //這里進(jìn)行排序,sort不準(zhǔn)傳參數(shù)

   unsigned int _ebp;
    __asm{
        mov _ebp,ebp
    }
    int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);
    for(int i=0;i<7;i++)
        cout<<p[i]<<endl;

}
int main()

 int s=0;
 int ss=0;
 char *str="fdsafdsafdsafdsafdsafdsafdsa";
 char fdsa='f';

 char srt[6];
    int arr[]={32,43,3,567,987,21,56};//數(shù)值隨即

    print();
    return 0;
}

運(yùn)行結(jié)果為:

如果我們修改了 char srt[6];之后去把int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);修改為int *p=(int *)(*(int *)_ebp-4-4-4-4-6-7*4);,,注意紅色部分的對(duì)比,,運(yùn)行結(jié)果就變?yōu)榱耍?/span>

顯然對(duì)比可知運(yùn)行結(jié)果出錯(cuò)了。在此多次一舉的給出對(duì)比無(wú)非是為了大家能夠?qū)ψ止?jié)的對(duì)齊方式加以重視,。當(dāng)然以上內(nèi)容難免有錯(cuò),,畢竟c語(yǔ)言博大精深,如果有不正確的地方,,請(qǐng)糾正,。 

如需轉(zhuǎn)載,請(qǐng)注明出處,!

如果你也喜歡C語(yǔ)言,,跟我一樣對(duì)于C語(yǔ)言想知其然更想知其所以然,,那么歡迎你來(lái)我的博客空間閱讀我的其他C語(yǔ)言文章,。我的博客地址:http://blog.csdn.net/bigloomy

 
分享到:

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多