亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

堆棧與函數調用

系統 1632 0

作者 xgywinner 日期 2009-3-18 11:44:00

1) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,一般使用寄存器來存取,效率很高,但是分配的內存容量有限。
2) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete來釋放內存。動態內存的生存期由程序員自己決定,使用非常靈活。
3) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
4) 文字常量分配在文字常量區,程序結束后由系統釋放。
5)程序代碼區。

經典實例:(代碼來自網絡高手,沒有找到原作者)

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #i nclude < string >

int a = 0 ; // 全局初始化區
char * p1; // 全局未初始化區
void main()
{
int b; //
char s[] = " abc " ; //
char * p2; //
char * p3 = " 123456 " ; // 123456/0在常量區,p3在棧上。
static int c = 0 ; // 全局(靜態)初始化區
p1 = ( char * )malloc( 10 );
p2
= ( char * )malloc( 20 ); // 分配得來得10和20字節的區域就在堆區。
strcpy(p1, " 123456 " ); // 123456/0放在常量區,編譯器可能會將它與p3所向"123456/0"優化成一個地方。
}

二 三種內存對象的比較
  棧對象的優勢是在適當的時候自動生成,又在適當的時候自動銷毀,不需要程序員操心;而且棧對象的創建速度一般較堆對象快,因為分配堆對象時,會調用operator new操作,operator new會采用某種內存空間搜索算法,而該搜索過程可能是很費時間的,產生棧對象則沒有這么麻煩,它僅僅需要移動棧頂指針就可以了。但是要注意的是,通常棧空間容量比較小,一般是1MB~2MB,所以體積比較大的對象不適合在棧中分配。特別要注意遞歸函數中最好不要使用棧對象,因為隨著遞歸調用深度的增加,所需的棧空間也會線性增加,當所需棧空間不夠時,便會導致棧溢出,這樣就會產生運行時錯誤。
  堆對象創建和銷毀都要由程序員負責,所以,如果處理不好,就會發生內存問題。如果分配了堆對象,卻忘記了釋放,就會產生內存泄漏;而如 果已釋放了對象,卻沒有將相應的指針置為NULL,該指針就是所謂的“懸掛指針”,再度使用此指針時,就會出現非法訪問,嚴重時就導致程序崩潰。但是高效的使用堆對象也可以大大的提高代碼質量。比如,我們需要創建一個大對象,且需要被多個函數所訪問,那么這個時候創建一個堆對象無疑是良好的選擇,因為我們通過在各個函數之間傳遞這個堆對象的指針,便可以實現對該對象的共享,相比整個對象的傳遞,大大的降低了對象的拷貝時間。另外,相比于棧空間,堆的容量要大得多。實際上,當物理內存不夠時,如果這時還需要生成新的堆對象,通常不會產生運行時錯誤,而是系統會使用虛擬內存來擴展實際的物理內存。
  靜態存儲區。所有的靜態對象、全局對象都于靜態存儲區分配。關于全局對象,是在main()函數執行前就分配好了的。其實,在main()函數中的顯示代 碼執行之前,會調用一個由編譯器生成的_main()函數,而_main()函數會進行所有全局對象的的構造及初始化工作。而在main()函數結束之 前,會調用由編譯器生成的exit函數,來釋放所有的全局對象。比如下面的代碼:

void main(void)
{
… …// 顯式代碼
}

實際上,被轉化成這樣:

void main(void)
{
_main(); //隱式代碼,由編譯器產生,用以構造所有全局對象
… … // 顯式代碼
… …
exit() ; // 隱式代碼,由編譯器產生,用以釋放所有全局對象
}

  除了全局靜態對象,還有局部靜態對象通和class的靜態成員,局部靜態對象是在函數中定義的,就像棧對象一樣,只不過,其前面多了個static關鍵字。局部靜態對象的生命期是從其所在函數第一次被調用,更確切地說,是當第一次執行到該靜態對象的聲明代碼時,產生該靜態局部對象,直到整個程序結束時,才銷毀該對象。class的靜態成員的生命周期是該class的第一次調用到程序的結束。

三 函數調用與堆棧

1)編譯器一般使用棧來存放函數的參數,局部變量等來實現函數調用。有時候函數有嵌套調用,這個時候棧中會有多個函數的信息,每個函數占用一個連續的區域。一個函數占用的區域被稱作幀()。同時棧是線程獨立的,每個線程都有自己的棧。例如下面簡單的函數調用:

堆棧與函數調用

另外函數堆棧的清理方式決定了當函數調用結束時由調用函數或被調用函數來清理函數幀,在VC中對函數棧的清理方式由兩種:


參數傳遞順序 誰負責清理參數占用的堆棧
__stdcall 從右到左 被調函數
__cdecl 從右到左 調用者

2) 有了上面的知識為鋪墊,我們下面細看一個函數的調用時堆棧的變化:

代碼如下:

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> int Add( int x, int y)
{
return x + y;
}

void main()
{
int * pi = new int ( 10 );
int * pj = new int ( 20 );
int result = 0 ;
result
= Add( * pi, * pj);
deletepi;
deletepj;
}

對上面的代碼,我們分為四步,當然我們只畫出了我們的代碼對堆棧的影響,其他的我們假設它們不存在,哈哈!

第一, int * pi = new int ( 10 ); int * pj = new int ( 20 ); int result = 0 ; 堆棧變化如下:

堆棧與函數調用

第二, Add( * pi, * pj);堆棧如下:

堆棧與函數調用

第三,將Add的結果給result,堆棧如下:

堆棧與函數調用

第四, deletepi; deletepj; 堆棧如下:

堆棧與函數調用

第五,當main()退出后,堆棧如下,等同于main執行前,哈哈!

堆棧與函數調用

四 完!

堆棧與函數調用


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产区一区 | 国产午夜永久福利视频在线观看 | 中文字幕亚洲高清综合 | 日本视频在线观看不卡高清免费 | 亚洲爱爱久久精品 | 日韩男女视频 | 午夜探花| 久久福利网 | 日韩欧美国产综合 | 国产视频一区在线观看 | 色妞欧美 | 涩涩伊人 | 欧美一级网址 | 日韩欧美精品综合一区二区三区 | 色噜噜五月综合激情久久爱 | 国产精品视频福利视频网 | 欧美精品videossex最新 | 六月婷婷啪啪 | 亚洲国产精品一区二区三区 | 91手机视频在线观看 | 欧美jizzhd欧美巨大 | 欧美久久综合九色综合 | 欧美xvideosexo另类 | 毛片片| 亚洲精品二三区伊人久久 | 日本视频播放免费线上观看 | 成年女人18毛片毛片免费 | 热久久精品免费视频 | 国产亚洲精品97在线观看 | 一级a欧美毛片 | 久久久精品久久久久三级 | 亚洲免费高清视频 | 欧美一级毛片视频 | 婷婷性 | 日韩三级一区二区 | 草草国产成人免费视频 | 99精品久久99久久久久 | 国产大片91精品免费观看不卡 | 久久福利资源网站免费看 | 精品日韩视频 | 欧美一级在线播放 |