棧大小和內存分部問題


今天面試問了一個棧大小問題,問過兩次內存的結構問題,都沒有答好,這次要弄清楚才行。

棧大小是有默認值的,如果申請的臨時變量太大的話就會超過棧大小,造成棧溢出。

編譯期限制棧大小,和系統限制棧深度根本是兩回事。系統限制棧深是限制進程主線程的棧深,限制的是整個函數調用鏈的最大棧深,這個棧深是函數調用鏈上各個函數棧幀大小之和。編譯期限制棧大小是限制單個函數棧幀的大小。

一、修改棧大小

棧的大小可以修改的。在應用程序我們經常需要定義大的數組,數組定義成局部變量非靜態變量,那么數組就會在棧上分配,當數組超過默認棧的大小時,會引起非常內存訪問。那么如何修改系統默認的棧的大小呢。

一般,在Unix-like平台,棧的大小不是由程序自己來控制的而是由環境變量來控制的,所以就不能通過設置編譯器(像gcc)的任何編譯標志來設置棧的大小;在windows平台下,棧的大小的信息是包含在可執行文件中的。它可以在Visual C++的編譯過程中設置,但是在gcc中是不可行的。

方法為

項目->屬性->鏈接器->系統->堆棧保留大小

注:這里填的是字節數

 

在一般情況下, 不同平台默認棧大小如下(僅供參考)

SunOS/Solaris 8172K bytes (Shared Version)

Linux 10240K bytes

Windows 1024K bytes (Release Version)

AIX 65536K bytes

如果定義大數組的情況下,那就需要修改默認的棧大小,下面給出幾個平台的修改方法:

1.SunOS/Solaris系統:

limit # 顯示當前用戶的棧大小 

unlimit # 將當前用戶的棧大小改為不限制大小

setenv STACKSIZE 32768 #設置當前用戶的棧大小為 32M bytes

 

2.Linux系統:

ulimit -a #顯示當前用戶的棧大小

ulimit -s 32768 #將當前用戶的棧大小設置為32M bytes

 

3. Windows (在編譯過程中的設置):

1). 選擇 "Project->Setting".

2). 選擇 "Link".

3. 選擇 "Category"中的 "Output".

 

4. 在 "Stack allocations"中的"Reserve:"中輸棧的大小,例如: 32768 

在 Visual Studio 開發環境中設置此鏈接器選項

  • 打開此項目的“屬性頁”對話框。有關詳細信息,請參見設置 Visual C++ 項目屬性
  • 單擊“鏈接器”文件夾。
  • 單擊“系統”屬性頁。
  • 修改下列任一屬性:
    • 堆棧提交大小
    • 堆棧保留大小 

問題解答:

方法一:STACKSIZE   定義.def文件

 

     語法:STACKSIZE reserve[,commit]

     reserve:棧的大小;commit:可選項,與操作系統有關,在NT上只一次分配物理內存的大小

 

方法二:設定/STACK

     VC6.0修改:

     打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后

在Reserve中設定堆棧的最大值和commit。

注意:reserve默認值為1MB,最小值為4Byte;commit是保留在虛擬內存的頁文件里面,它設置的較

大會使棧開辟較大的值,可能增加內存的開銷和啟動時間

二、堆大小

堆大小是可以自己申請的,只要不超過內存都是可以的。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低(雖然程序在退出后操作系統會對內存進行回收管理)。對於棧來講,則不會存在這個問題。

三、C程序內存分配

C程序一般分為
1.程序段:程序段為程序代碼在內存中的映射.一個程序可以在內存中多有個副本.
2.初始化過的數據:在程序運行值初已經對變量進行初始化的
3.未初始化過的數據:在程序運行初未對變量進行初始化的數據
4.堆(stack):存儲局部,臨時變量,在程序塊開始時自動分配內存,結束時自動釋放內存.存儲函數的返回指針.
5.棧(heap):存儲動態內存分配,需要程序員手工分配,手工釋放.
6.文字常量區—常量字符串就是放在這里的。程序結束后由系統釋放
 
附程序分布圖:
[轉載]c程序內存分布

這是一個前輩寫的,非常詳細
//main.cpp
  int a=0;    //全局初始化區
  char *p1;   //全局未初始化區
  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"優化成一個地方。
}

3.1 申請效率的比較:

棧:由系統自動分配,速度較快。但程序員是無法控制的。
堆:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
另外,在WINDOWS下,最好的方式是用Virtual Alloc分配內存,他不是在堆,也不是在棧,而是直接在進

程的地址空間中保留一塊內存,雖然用起來最不方便。但是速度快,也最靈活。
3.2 堆和棧中的存儲內容
棧:在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的

地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變

量。注意靜態變量是不入棧的。
當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主

函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。
3.3 存取效率的比較

char s1[]="aaaaaaaaaaaaaaa";
char *s2="bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的;
而bbbbbbbbbbb是在編譯時就確定的;
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。


免責聲明!

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



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