【Java小疑問】java變數儲存的位置(雜)

love_Aym發表於2018-06-05

https://blog.csdn.net/ma_chen_qq/article/details/72870849

1.暫存器:最快的儲存區, 由編譯器根據需求進行分配,我們在程式中無法控制. 
2. 棧:存放基本型別的變數資料和物件的引用,但物件本身不存放在棧中,而是存放在堆(new 出來的物件)或者常量池中(字串常量物件存放在常量池中。) 
3. 堆:存放所有new出來的物件。 
4. 靜態域:存放靜態成員(static定義的) 
5. 常量池:存放字串常量和基本型別常量(public static final)。 
6. 非RAM儲存:硬碟等永久儲存空間 

  這裡我們主要關心棧,堆和常量池,對於棧和常量池中的物件可以共享,對於堆中的物件不可以共享。棧中的資料大小和生命週期是可以確定的,當沒有引用指向資料時,這個資料就會消失。堆中的物件的由垃圾回收器負責回收,因此大小和生命週期不需要確定,具有很大的靈活性。 
  對於字串:其物件的引用都是儲存在棧中的,如果是編譯期已經建立好(直接用雙引號定義的)的就儲存在常量池中,如果是執行期(new出來的)才能確定的就儲存在堆中。對於equals相等的字串,在常量池中永遠只有一份,在堆中有多份。 
  如以下程式碼:
對於通過new產生一個字串(假設為”china”)時,會先去常量池中查詢是否已經有了”china”物件,如果沒有則在常量池中建立一個此字串物件,然後堆中再建立一個常量池中此”china”物件的拷貝物件。這也就是有道面試題:String s = new String(“xyz”);產生幾個物件?一個或兩個,如果常量池中原來沒有”xyz”,就是兩個。 

對於基礎型別的變數和常量:變數和引用儲存在棧中,常量儲存在常量池中。 
如以下程式碼:

String s1 = "china";    
String s2 = "china";    
String s3 = "china";    
String ss1 = new String("china");    
String ss2 = new String("china");    
String ss3 = new String("china"); 
對於通過new產生一個字串(假設為”china”)時,會先去常量池中查詢是否已經有了”china”物件,如果沒有則在常量池中建立一個此字串物件,然後堆中再建立一個常量池中此”china”物件的拷貝物件。這也就是有道面試題:String s = new String(“xyz”);產生幾個物件?一個或兩個,如果常量池中原來沒有”xyz”,就是兩個。 
對於基礎型別的變數和常量:變數和引用儲存在棧中,常量儲存在常量池中。 
如以下程式碼:
int i1 = 9;    
int i2 = 9;    
int i3 = 9;     
public static final int INT1 = 9;    
public static final int INT2 = 9;    
public static final int INT3 = 9; 
對於成員變數和區域性變數:成員變數就是方法外部,類的內部定義的變數;區域性變數就是方法或語句塊內部定義的變數。區域性變數必須初始化。形式引數是區域性變數,區域性變數的資料存在於棧記憶體中。棧記憶體中的區域性變數隨著方法的消失而消失。 

成員變數儲存在堆中的物件裡面,由垃圾回收器負責回收。 

如以下程式碼:

class BirthDate {    
    private int day;    
    private int month;    
    private int year;        
    public BirthDate(int d, int m, int y) {    
        day = d;     
        month = m;     
        year = y;    
    }    
//    省略get,set方法………    
}    
    
public class Test{    
    public static void main(String args[]){    
int date = 9;    
        Test test = new Test();          
           test.change(date);     
        BirthDate d1= new BirthDate(7,7,1970);           
    }      
    
    public void change1(int i){    
        i = 1234;    
    }  

對於以上這段程式碼,date為區域性變數,i,d,m,y都是形參為區域性變數,day,month,year為成員變數。下面分析一下程式碼執行時候的變化: 
1. main方法開始執行:int date = 9; 
date區域性變數,基礎型別,引用和值都存在棧中。 
2. Test test = new Test(); 
test為物件引用,存在棧中,物件(new Test())存在堆中。 
3. test.change(date); 
i為區域性變數,引用和值存在棧中。當方法change執行完成後,i就會從棧中消失。 
4. BirthDate d1= new BirthDate(7,7,1970);  
d1 為物件引用,存在棧中,物件(new BirthDate())存在堆中,其中d,m,y為區域性變數儲存在棧中,且它們的型別為基礎型別,因此它們的資料也儲存在棧中。 day,month,year為成員變數,它們儲存在堆中(new BirthDate()裡面)。當BirthDate構造方法執行完之後,d,m,y將從棧中消失。 

5.main方法執行完之後,date變數,test,d1引用將從棧中消失,new Test(),new BirthDate()將等待垃圾回收。



final修飾的區域性變數是存放在棧中還是在常量池中 

存放在常量池中。首先final關鍵字對於變數的儲存區域是沒有任何影響的。jvm規範中,類的靜態變數儲存在方法區,
例項變數儲存在堆區。也就是說static關鍵字才對變數的儲存區域造成影響。final關鍵字來修飾變數表明瞭該變數一
旦賦值就無法更改。在Java中你可以這樣理解:所有的變數,包括基本型別和引用型別,它們的變數都是存放在棧中
,棧中的每個變數都包含型別、名稱、值這些內容,只不過基本型別變數的值為一個具體的值,而引用型別的變數的
值為物件在堆中的地址。
java中的常量池技術,是為了方便快捷地建立某些物件而出現的,當需要一個物件時,就可以從池中取一個出來(如果池中沒有則建立一個),則在需要重複建立相等變數時節省了很多時間。常量池其實也就是一個記憶體空間,不同於使用new關鍵字建立的物件所在的堆空間
轉自:http://www.iteye.com/topic/634530
1.暫存器:最快的儲存區, 由編譯器根據需求進行分配,我們在程式中無法控制.
2. 棧:存放基本型別的變數資料和物件的引用,但物件本身不存放在棧中,而是存放在堆(new 出來的物件)或者常量池中(物件可能在常量池裡)(字串常量物件存放在常量池中。)
3. 堆:存放所有new出來的物件。
4. 靜態域:存放靜態成員(static定義的)
5. 常量池:存放字串常量和基本型別常量(public static final)。有時,在嵌入式系統中,常量本身會和其他部分分割離開(由於版權等其他原因),所以在這種情況下,可以選擇將其放在ROM中 。
6. 非RAM儲存:硬碟等永久儲存空間

這裡我們主要關心棧,堆和常量池,對於棧和常量池中的物件可以共享,對於堆中的物件不可以共享。棧中的資料大小和生命週期是可以確定的,當沒有引用指向資料時,這個資料就會消失。堆中的物件的由垃圾回收器負責回收,因此大小和生命週期不需要確定,具有很大的靈活性。
對於字串:其物件的引用都是儲存在棧中的,如果是編譯期已經建立好(直接用雙引號定義的)的就儲存在常量池中,如果是執行期(new出來的)才能確定的就儲存在堆中。對於equals相等的字串,在常量池中永遠只有一份,在堆中有多份。
如以下程式碼:
String s1 = "china";
String s2 = "china";
String s3 = "china";
String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
這裡解釋一下黃色這3個箭頭,對於通過new產生一個字串(假設為”china”)時,會先去常量池中查詢是否已經有了”china”物件,如果沒有則在常量池中建立一個此字串物件,然後堆中再建立一個常量池中此”china”物件的拷貝物件。這也就是有道面試題:String s = new String(“xyz”);產生幾個物件?一個或兩個,如果常量池中原來沒有”xyz”,就是兩個。


對於基礎型別的變數和常量:變數和引用儲存在棧中,常量儲存在常量池中。
如以下程式碼:

int i1 = 9;
int i2 = 9;
int i3 = 9;
public static final int INT1 = 9;
public static final int INT2 = 9;
public static final int INT3 = 9;

對於成員變數和區域性變數:成員變數就是方法外部,類的內部定義的變數;區域性變數就是方法或語句塊內部定義的變數。區域性變數必須初始化。
形式引數是區域性變數,區域性變數的資料存在於棧記憶體中。棧記憶體中的區域性變數隨著方法的消失而消失。
成員變數儲存在堆中的物件裡面,由垃圾回收器負責回收。
注意:棧裡只有一個9 ,i1,i2,i3 都指向9 。如果 令 i2=7;會在棧裡生成7 再令i2 指向7。
如以下程式碼:

class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
}
public void change1(int i){
i = 1234;
}
}

 
對於以上這段程式碼,date為區域性變數,i,d,m,y都是形參為區域性變數,day,month,year為成員變數。下面分析一下程式碼執行時候的變化:
1. main方法開始執行:int date = 9;
date區域性變數,基礎型別,引用和值都存在棧中。
2. Test test = new Test();
test為物件引用,存在棧中,物件(new Test())存在堆中。
3. test.change(date);
i為區域性變數,引用和值存在棧中。當方法change執行完成後,i就會從棧中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
d1為物件引用,存在棧中,物件(new BirthDate())存在堆中,其中d,m,y為區域性變數儲存在棧中,且它們的型別為基礎型別,因此它們的資料也儲存在棧中。day,month,year為成員變數,它們儲存在堆中(new BirthDate()裡面)。當BirthDate構造方法執行完之後,d,m,y將從棧中消失。
5.main方法執行完之後,date變數,test,d1引用將從棧中消失,new Test(),new BirthDate()將等待垃圾回收
------------------------------------------------------------------------------------------------------------------------------
JVM 中的堆疊
JVM是基於堆疊的虛擬機器.JVM為每個新建立的執行緒都分配一個堆疊.也就是說,對於一個Java程式來說,它的執行就是通過對堆疊的操作來完成的。堆疊以幀為單位儲存執行緒的狀態。JVM對堆疊只進行兩種操作:以幀為單位的壓棧和出棧操作。
  我們知道,某個執行緒正在執行的方法稱為此執行緒的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當執行緒啟用一個Java方法,JVM就會線上程的 Java堆疊裡新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來儲存引數,區域性變數,中間計算過程和其他資料.這個幀在這裡和編譯原理中的活動紀錄的概念是差不多的.
  從Java的這種分配機制來看,堆疊又可以這樣理解:堆疊(Stack)是作業系統在建立某個程式時或者執行緒(在支援多執行緒的作業系統中是執行緒)為這個執行緒建立的儲存區域,該區域具有先進後出的特性。
  每一個Java應用都唯一對應一個JVM例項,每一個例項唯一對應一個堆。應用程式在執行中所建立的所有類例項或陣列都放在這個堆中,並由應用所有的執行緒共享.跟C/C++不同,Java中分配堆記憶體是自動初始化的。Java中所有物件的儲存空間都是在堆中分配的,但是這個物件的引用卻是在堆疊中分配,也就是說在建立一個物件時從兩個地方都分配記憶體,在堆中分配的記憶體實際建立這個物件,而在堆疊中分配的記憶體只是一個指向這個堆物件的指標(引用)而已。


JAVA 堆疊
棧與堆都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式設計師不能直接地設定棧或堆。
  Java的堆是一個執行時資料區,類的(物件從中分配空間。這些物件通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程式程式碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶體的,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在執行時動態分配記憶體,存取速度較慢。
  棧的優勢是,存取速度比堆要快,僅次於暫存器,棧資料可以共享。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本型別的變數(,int, short, long, byte, float, double, boolean, char)和物件控制程式碼。
  棧有一個很重要的特殊性,就是存在棧中的資料可以共享。假設我們同時定義:
  int a = 3;
  int b = 3;
  編譯器先處理int a = 3;首先它會在棧中建立一個變數為a的引用,然後查詢棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在建立完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜尋棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種資料的共享與兩個物件的引用同時指向一個物件的這種共享是不同的,因為這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個物件引用變數修改了這個物件的內部狀態,會影響到另一個物件引用變數
----------------------------------------------------------------------------------------------
另外還有一個疑問:
int [] arr={1,2,3,4}
即沒有用new 顯示生成的原始型別陣列是存放在哪的。
是存放在堆還是棧裡。
在c++ 裡肯定是在棧裡(C++ 在堆裡生成的一定要手動delete 掉自己回收的,而int a[]={1,3}我們不需手動回收)


區域性變數,全域性變數,靜態變數

區域性變數

func()

{

int a;

int b=0;

}

很多書籍中也叫自動變數,它宣告在函式塊內,作用範圍也在函式塊內。 不能被同一原始檔的其他函式使用,也不能被其他檔案中的函式使用。

區域性變數儲存在棧中。無論區域性變數顯示初始化(如b),或者未初始化(如a),都只有當定義它們的程式塊被呼叫時(即執行時),才分配空間,宣告或定義時並不分配,區域性變數不是可執行模組的一部分!!除非顯示地對區域性變數進行初始化,否則,它們的初始值是不確定的。

全域性變數

int a;

int b=0;

func()

{

}

全域性變數沒有宣告在任何一個函式內,作用範圍在程式執行始終存在。

能被同一原始檔的任何函式使用,也能被其他檔案中的函式使用,但要使用extern關鍵字。

全域性變數儲存在資料段中。全域性變數顯示初始化時(如b),或者未初始化時(如a),在程式映像中有不同的分割槽:已初始化的全域性變數是可執行模組的一部分。未初始化的全域性變數則不是可執行模組的一部分,只有當定義它們的程式被呼叫時(即執行時),才分配空間,宣告或定義時並不分配。未初始化的全域性變數在執行時被初始化為0。

靜態變數

用static關鍵字定義的變數,與區域性變數相比, static區域性變數有三點不同:
1. 儲存空間分配不同
auto型別分配在棧上,屬於動態儲存類別,佔動態儲存區空間,函式呼叫結束後自動釋放, 而static分配在靜態儲存區,在程式整個執行期間都不釋放,兩者之間的作用域相同,但生存期不同。
2. static區域性變數在所處模組在初次執行時進行初始化工作,且只操作一次。
3. 對於區域性靜態變數,如果不賦初值,編譯期會自動賦初值0或空字元,而auto型別的初值是不確定的。(對於C++中的class物件例外, class的物件例項如果不初始化, 則會自動呼叫預設建構函式, 不管是否是static型別)

靜態全域性變數用來表示不能被其它檔案訪問的全域性變數和函式。為了限制全域性變數/函式的作用域,函式或變數前加static使得函式成為靜態函式。但此處“static”的含義不是指儲存方式,而是指對函式的作用域僅侷限於本檔案(所以又稱內部函式)。注意:對於外部(全域性)變數,不論是否有static限制,它的儲存區域都是在靜態儲存區,生存期都是全域性的。此時的static只是起作用域限制作用,限定作用域在本模組(檔案)內部。

靜態全域性變數與全域性變數的差別是:靜態全域性變數只能被同一原始檔中的函式呼叫,其他檔案中的函式不能呼叫靜態全域性變數。

主存中的程式映像佈局

高階地址(小地址)| 命令列引數和環境變數|    <--argc、argv、環境

                                |                 棧                |     <--函式呼叫的活動記錄(返回地址引數,已儲存的暫存器,區域性變數)

                               |             ..........             |

                                |                 堆                |    <--用malloc函式族分配的記憶體

                                | 未初始化的全域性變數   |

                                | 已初始化的全域性變數   |

低端地址(大地址)|     程式文字                |


區域性變數,靜態區域性變數,全域性變數,靜態全域性變數在記憶體中的存放區別(轉)

 我們先來看記憶體中的幾大區:  記憶體到底分幾個區?


下面有幾種網上的理解,我整理一下:

一:

1、棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

2、堆區(heap) — 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由os回收 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列,呵呵。

3、全域性區(靜態區)(static)—,全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程式結束後有系統釋放。

4、文字常量區 —常量字串就是放在這裡的。 程式結束後由系統釋放。

5、程式程式碼區—存放函式體的二進位制程式碼。 

二:

 1、棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變數的儲存區。裡面的變數通常是區域性變數、函式引數等。

 2、堆,就是那些由new分配的記憶體塊,他們的釋放編譯器不去管,由我們的應用程式去控制,一般一個new就要對應一個delete。如果程式設計師沒有釋放掉,那麼在程式結束後,作業系統會自動回收。

 3、自由儲存區,就是那些由malloc等分配的記憶體塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。

 4、全域性/靜態儲存區,全域性變數和靜態變數被分配到同一塊記憶體中,在以前的C語言中,全域性變數又分為初始化的和未初始化的,在c++裡面沒有這個區分了,他們共同佔用同一塊記憶體區。

 5、常量儲存區,這是一塊比較特殊的儲存區,他們裡面存放的是常量,不允許修改。

三:

 1、bss是英文block started by symbol的簡稱,通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域,在程式載入時由核心清0。bss段屬於靜態記憶體分配。它的初始值也是由使用者自己定義的連線定位檔案所確定,使用者應該將它定義在可讀寫的ram區內,源程式中使用malloc分配的記憶體就是這一塊,它不是根據data大小確定,主要由程式中同時分配記憶體最大值所確定,不過如果超出了範圍,也就是分配失敗,可以等空間釋放之後再分配。

 2、text段是程式程式碼段,在at91庫中是表示程式段的大小,它是由編譯器在編譯連線時自動計算的,當你在連結定位檔案中將該符號放置在程式碼段後,那麼該符號表示的值就是程式碼段大小,編譯連線時,該符號所代表的值會自動代入到源程式中。

 3、data包含靜態初始化的資料,所以有初值的全域性變數和static變數在data區。段的起始位置也是由連線定位檔案所確定,大小在編譯連線時自動分配,它和你的程式大小沒有關係,但和程式使用到的全域性變數,常量數量相關。

 4、stack儲存函式的區域性變數和引數。是一種“後進先出”(last in first out,lifo)的資料結構,這意味著最後放到棧上的資料,將會是第一個從棧上移走的資料。對於哪些暫時存貯的資訊,和不需要長時間儲存的資訊來說,lifo這種資料結構非常理想。在呼叫函式或過程後,系統通常會清除棧上儲存的區域性變數、函式呼叫資訊及其它的資訊。棧另外一個重要的特徵是,它的地址空間“向下減少”,即當棧上儲存的資料越多,棧的地址就越低。棧(stack)的頂部在可讀寫的ram區的最後。

 

 5、heap儲存函式內部動態分配記憶體,是另外一種用來儲存程式資訊的資料結構,更準確的說是儲存程式的動態變數。堆是“先進先出”(first in first out,fifo)資料結構。它只允許在堆的一端插入資料,在另一端移走資料。堆的地址空間“向上增加”,即當堆上儲存的資料越多,堆的地址就越高。

 

總結(不確定!!!):

 研究這個意義不大,不同編譯器,可能行為不同,如果是vc的話,基本上如下:

 1、程式碼區,是編譯器生成的一個exe區段,擁有可讀和可執行屬性,但是實際上如果不開dep資料執行保護,所有的區段都是可執行的。

 2、所謂的棧區,低地址(小於exe基地址),擁有可讀寫屬性,exe中沒有對應的區段,系統載入dll時自動生成,由於記憶體地址使用方式從大往小減,所以數量有限,儘量不要定義過大的陣列變數。 const的區域性變數也是放在棧裡的,而不是放在常量區。

 3、所謂的堆區,就是malloc和new之類的記憶體所在區段,擁有可讀寫屬性,exe中沒有對應的區段,系統載入dll時自動生成,首先是利用棧區地址下面的區段,也是低地址,當用完了,會自動分配稍微高一點地址(大於exe基地址)。 malloc和new都在這裡分配記憶體。

 4、全域性資料區,是編譯器生成的一個exe區段,擁有可讀寫屬性,初始和未初始化的全域性和靜態變數都放在這裡。

 5、常量區,是編譯器生成的一個exe區段,只有可讀屬性,比如char s = " hello world" ,這時候" hello world" 就在常量區,由於沒有可寫屬性,所以修改內容會出錯,另外全域性的const變數也放在常量區裡,這和c++程式設計語言裡對const變數存放位置是不符合的,因為儲存器各有各的差異。

 

區域性變數,區域性靜態變數,全域性變數,全域性靜態變數區別:

區域性變數:    棧區
區域性靜態變數:靜態區
全域性變數:    靜態區的常量區
全域性靜態變數:靜態區

在進行C/C++程式設計時,需要程式設計師對記憶體的瞭解比較精準。經常需要操作的記憶體可分為以下幾個類別: 
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。 
2、堆區(heap) — 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列,呵呵。 
3、全域性區(靜態區)(static)—,全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程式結束後有系統釋放 
4、文字常量區 —常量字串就是放在這裡的。 程式結束後由系統釋放 
5、程式程式碼區—存放函式體的二進位制程式碼。 
以下是一段實際說明的程式程式碼:

這是一個前輩寫的,非常詳細 

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


二、堆和棧的理論知識 

2.1申請方式 
stack: 由系統自動分配。 例如,宣告在函式中一個區域性變數 int b; 系統自動在棧中為b開闢空間 
heap: 需要程式設計師自己申請,並指明大小,在c中malloc函式 
如p1 = (char *)malloc(10); 
在C++中用new運算子 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在棧中的。 


2.2 
申請後系統的響應 

棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢位。 

堆:首先應該知道作業系統有一個記錄空閒記憶體地址的連結串列,當系統收到程式的申請時, 
會遍歷該連結串列,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點連結串列中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首地址處記錄本次分配的大小,這樣,程式碼中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒連結串列中。 


2.3申請大小的限制 

棧:在Windows下,棧是向低地址擴充套件的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。 
堆:堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。這是由於系統是用連結串列來儲存的空閒記憶體地址的,自然是不連續的,而連結串列的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。 


2.4申請效率的比較:  www.2cto.com

棧由系統自動分配,速度較快。但程式設計師是無法控制的。 
堆是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便. 
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配記憶體,他不是在堆,也不是在棧是直接在程式的地址空間中保留一快記憶體,雖然用起來最不方便。但是速度, 也最靈活 


2.5堆和棧中的儲存內容 

棧: 在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址,然後是函式的各個引數,在大多數的C編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。 
當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續執行。 
堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容有程式設計師安排。 


2.6存取效率的比較

char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在執行時刻賦值的; 
而bbbbbbbbbbb是在編譯時就確定的; 
但是,在以後的存取中,在棧上的陣列比指標所指向的字串(例如堆)快。 
比如: 
#include 
void main() 

char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 

對應的彙編程式碼 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 
第一種在讀取時直接就把字串中的元素讀到暫存器cl中,而第二種則要先把指edx中,在根據edx讀取字元,顯然慢了

 

小結: 
堆和棧的區別可以用如下的比喻來看出: 
使用棧就象我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。 

堆和棧的區別主要分: 
作業系統方面的堆和棧,如上面說的那些,不多說了。 
還有就是資料結構方面的堆和棧,這些都是不同的概念。這裡的堆實際上指的就是(滿足堆性質的)優先佇列的一種資料結構,第1個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或資料結構。 
雖然堆疊,堆疊的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由於歷史的原因。


區域性變數,全域性變數,靜態變數

區域性變數:很多書籍中也叫自動變數,它宣告在函式塊內,作用範圍也在函式塊內。 不能被同一原始檔的其他函式使用,也不能被其他檔案中的函式使用。

區域性變數儲存在棧中。無論區域性變數顯示初始化(如b),或者未初始化(如a),都只有當定義它們的程式塊被呼叫時(即執行時),才分配空間,宣告或定義時並不分配,區域性變數不是可執行模組的一部分!!除非顯示地對區域性變數進行初始化,否則,它們的初始值是不確定的。


全域性變數:全域性變數沒有宣告在任何一個函式內,作用範圍在程式執行始終存在。

能被同一原始檔的任何函式使用,也能被其他檔案中的函式使用,但要使用extern關鍵字。

全域性變數儲存在資料段中。全域性變數顯示初始化時(如b),或者未初始化時(如a),在程式映像中有不同的分割槽:已初始化的全域性變數是可執行模組的一部分。未初始化的全域性變數則不是可執行模組的一部分,只有當定義它們的程式被呼叫時(即執行時),才分配空間,宣告或定義時並不分配。未初始化的全域性變數在執行時被初始化為0。


靜態變數:用static關鍵字定義的變數,與區域性變數相比, static區域性變數有三點不同:

1. 儲存空間分配不同
auto型別分配在棧上,屬於動態儲存類別,佔動態儲存區空間,函式呼叫結束後自動釋放, 而static分配在靜態儲存區,在程式整個執行期間都不釋放,兩者之間的作用域相同,但生存期不同。
2. static區域性變數在所處模組在初次執行時進行初始化工作,且只操作一次。
3. 對於區域性靜態變數,如果不賦初值,編譯期會自動賦初值0或空字元,而auto型別的初值是不確定的。(對於C++中的class物件例外, class的物件例項如果不初始化, 則會自動呼叫預設建構函式, 不管是否是static型別)


靜態全域性變數用來表示不能被其它檔案訪問的全域性變數和函式。為了限制全域性變數/函式的作用域,函式或變數前加static使得函式成為靜態函式。但此處“static”的含義不是指儲存方式,而是指對函式的作用域僅侷限於本檔案(所以又稱內部函式)。注意:對於外部(全域性)變數,不論是否有static限制,它的儲存區域都是在靜態儲存區,生存期都是全域性的。此時的static只是起作用域限制作用,限定作用域在本模組(檔案)內部。

靜態全域性變數與全域性變數的差別是:靜態全域性變數只能被同一原始檔中的函式呼叫,其他檔案中的函式不能呼叫靜態全域性變數。


小結: 
堆和棧的區別可以用如下的比喻來看出: 
使用棧就象我們去飯館裡吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。 

堆和棧的區別主要分: 
作業系統方面的堆和棧,如上面說的那些,不多說了。 
還有就是資料結構方面的堆和棧,這些都是不同的概念。這裡的堆實際上指的就是(滿足堆性質的)優先佇列的一種資料結構,第1個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或資料結構。 
雖然堆疊,堆疊的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由於歷史的原因。


相關文章