棧空間與堆空間

2021-09-12 15:58:36 字數 4019 閱讀 1957

出處:

以下為正文:

乙個由c/c++編譯的程式占用的記憶體分為以下幾個部分:

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

2、堆區(heap):一般是由程式設計師分配釋放,若程式設計師不釋放的話,程式結束時可能由os**,值得注意的是他與資料結構的堆是兩回事,分配方式倒是類似於資料結構的鍊錶。

3、全域性區(static):也叫靜態資料記憶體空間,儲存全域性變數和靜態變數,全域性變數和靜態變數的儲存是放一塊的,初始化的全域性變數和靜態變數放一塊區域,沒有初始化的在相鄰的另一塊區域,程式結束後由系統釋放。

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

5、程式**區:存放函式體的二進位制**。

我們在程式中所定義的定義的區域性變數int、區域性陣列等都是儲存在棧空間中。棧空間具有乙個鮮明的特點:函式內定義的變數出了函式範圍,其所占用的記憶體空間自動釋放。但是,棧空間的尺寸有最大限制,不適合分配大空間使用

所以,因為棧空間出了函式範圍就釋放,所以不適合要給其他地方使用的記憶體需求。其最大的好處就在於:不用程式設計師手動釋放記憶體。

首先,我們來看看下面一段**,其中getdata函式返回了乙個int陣列型別的指標,而getdata2函式返回了另乙個int陣列型別的指標:

int *getdata()

; return nums;}

int *getdata2()

; return aaa;

}

我們在main函式中首先呼叫getdata函式,看看能否取得nums陣列的前三個元素:

int main(int argc, char *ar**)

執行結果如下圖所示,我們發現是ok的,原來可以將區域性變數的指標作為返回值返回呢!

但是,如果我們在呼叫getdata函式之後,又呼叫了getdata2函式呢,這時還能正確地列印nums陣列嗎?看看下面的執行順序:

int main(int argc, char *ar**)

在列印之前,我們又執行了getdata2函式,那麼執行結果呢,看看下圖吧:

這時,突然覺得,憂傷爆了!剛剛都還是好的啊!

那麼,問題來了,這是為什麼呢?我們剛剛提到了,棧是由系統自動分配和釋放,函式內部的區域性變數的生命週期就只是在函式週期內,只要函式執行完畢,那麼其內部的區域性變數的生命週期也就結束了。於是,當我們執行完第一句**後,nums指標所指向的陣列的那一塊記憶體區域可能就已經被釋放了,但是資料還未清理也就是還留在那兒。但是,當我們執行完第二句**後,在getdata2函式中又定義了乙個陣列aaa,它又將剛剛釋放的棧空間記憶體占用了,於是nums所指向的這塊區域就是aaa了。當執行完第二句**,aaa又被釋放了,但是其資料還在那裡並未清除,也就是我們前面幾篇提到的髒記憶體區域。所以,最後顯示的就是8,7,6而不是1,2,3了。

剛剛提到的棧空間最大的優點就是棧空間出了函式範圍就釋放,不需要程式設計師手動釋放,就像自動擋汽車一樣,都不用我們去加減檔變速。但是,如果我們想自己控制記憶體的分配呢?這時候,就可以使用堆空間來儲存,堆空間可以儲存棧空間無法儲存的大記憶體。這裡,我們可以借助malloc函式在堆空間中分配一塊指定大小的記憶體,用完之後,呼叫free函式及時釋放記憶體。

// malloc(要分配的位元組數)

int *nums = (int*)malloc(sizeof(int)*10);

nums[0]=1;

nums[1]=8;

free(nums);

需要注意的是:在malloc函式中需要指定要分配的記憶體所占用的位元組大小。

(1)在方法內malloc,用完了由呼叫者free

這裡我們可以結合malloc和free來解決我們在棧空間中所遇到的問題,重寫上面的**如下:

int *getdata()

int *getdata2()

int main(int argc, char *ar**)

這裡我們將所有要返回的指標都改為了使用malloc動態分配的,在main函式中呼叫free將記憶體手動釋放掉,來看看執行結果:

這下輸出的還是getdata函式返回的指標所指向的記憶體區域的資料,沒有出現交叉影響,完美!

(2)把區域性變數定義為static

char *getstr()

int main(int argc, char *ar**)

由本文開篇可知,除了棧空間和堆空間,還有一塊全域性區,它直到程式結束後才會釋放。

but,需要注意的是:不適合於多執行緒呼叫,如果想儲存返回內容,你需要呼叫者盡快複製乙份。

(3)(推薦)由呼叫者分配記憶體空間,只是把指標發給函式,函式內部把資料拷貝到記憶體中

這裡怎麼來理解呢,也就是三個步驟,第一步:由呼叫者分配記憶體空間;第二步:把指標傳遞給函式;第三步:函式內部把資料拷貝到記憶體中。下面我們通過乙個小案例:從檔名分析檔名和副檔名,來看看這三個步驟怎麼來實現。

// step3:函式內部把資料拷貝到記憶體中

void parsefilename(char* filename,char* name,char* ext)

// 記錄結尾的指標

char *endptr = ptr;

//ptr移動到了字串的結尾,再把ptr移動到"."的位置

while(*ptr != '.')

// 兩個指標相減表示兩個指標相隔的元素的個數

memcpy(name,filename,(ptr-filename)*sizeof(char));

memcpy(ext,ptr+1,(endptr-ptr)*sizeof(char));

}int main(int argc, char *ar**)

; char ext[20] = ;

// step2:只是把指標傳遞給函式

parsefilename(str,name,ext);

printf("解析完成:\n");

printf("檔名:%s,字尾:%s\n",name,ext);

return 0;

}

這種方法避免了函式返回指標,程式設計師手動分配的記憶體都是在棧空間中,然後函式內部處理後再將經過邏輯處理後的資料儲存到棧空間中的指定區域內,最後main函式中再訪問修改後的記憶體區域。這裡的執行結果如下圖所示:

雖然這個檔名有點**,但是功能還是完成了,哎喲,不錯哦!

如鵬網,《c語言也能幹大事(第三版)》

堆空間與棧空間的區別

1.棧區 stack 又編譯器自動分配釋放,存放函式的引數值,區域性變數的值等,其操作方式類似於資料結構的 棧。2.堆區 heap 一般是由程式設計師分配釋放,若程式設計師不釋放的話,程式結束時可能由os 值得注意的是他與 資料結構的堆是兩回事,分配方式倒是類似於資料結構的鍊錶。3.堆和棧的區別 1...

棧空間和堆空間

乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 又編譯器自動分配釋放,存放函式的引數值,區域性變數的值等,其操作方式類似於資料結構的棧。2 堆區 heap 一般是由程式設計師分配釋放,若程式設計師不釋放的話,程式結束時可能由os 值得注意的是他與資料結構的堆是兩回事,分配...

棧空間和堆空間

乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 又編譯器自動分配釋放,存放函式的引數值,區域性變數的值等,其操作方式類似於資料結構的棧。2 堆區 heap 一般是由程式設計師分配釋放,若程式設計師不釋放的話,程式結束時可能由os 值得注意的是他與資料結構的堆是兩回事,分配...