C語(yǔ)言深入講解棧與堆和靜態(tài)存儲(chǔ)區(qū)的使用
一、程序中的棧
- 棧是現(xiàn)代計(jì)算機(jī)程序里最為重要的概念之一
- 棧在程序中用于維護(hù)函數(shù)調(diào)用上下文
- 函數(shù)中的參數(shù)和局部變量存儲(chǔ)在棧上

棧保存了一個(gè)函數(shù)調(diào)用所需的維護(hù)信息
- 參數(shù)
- 返回地址
- 局部變量
- 調(diào)用上下文

二、函數(shù)的調(diào)用過(guò)程
每次函數(shù)調(diào)用都對(duì)應(yīng)著一個(gè)棧上的活動(dòng)記錄
- 調(diào)用函數(shù)的活動(dòng)記錄位于棧的中部
- 被調(diào)函數(shù)的活動(dòng)記錄位于棧的頂部

三、函數(shù)調(diào)用的棧變化
從main() 開(kāi)始運(yùn)行

main() 調(diào)用 f()

當(dāng)從 f() 調(diào)用中返回 main()

四、函數(shù)調(diào)用棧上的數(shù)據(jù)
- 函數(shù)調(diào)用時(shí),對(duì)應(yīng)的棧空間在函數(shù)返回前是專(zhuān)用的
- 函數(shù)調(diào)用結(jié)束后,棧空間將被釋放,數(shù)據(jù)不再有效

下面看一個(gè)指向棧數(shù)據(jù)的指針:
#include <stdio.h>
int* g()
{
int a[10] = {0};
return a;
}
void f()
{
int i = 0;
int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *pointer = g();
for (i = 0; i < 10; i++)
{
b[i] = pointer[i];
}
for(i = 0; i < 10; i++)
{
printf("%d\n", b[i]);
}
}
int main()
{
f();
return 0;
}輸出結(jié)果如下:

如果把
for (i = 0; i < 10; i++)
{
b[i] = pointer[i];
}
注釋了,直接打印 pointer[i] 里面的數(shù)據(jù),如下:
#include <stdio.h>
int* g()
{
int a[10] = {0};
return a;
}
void f()
{
int i = 0;
int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *pointer = g();
/*
for (i = 0; i < 10; i++)
{
b[i] = pointer[i];
}
*/
for(i = 0; i < 10; i++)
{
printf("%d\n", pointer[i]);
}
}
int main()
{
f();
return 0;
}輸出結(jié)果如下:

為什么直接打印 pointer[i] 里面的值會(huì)是這樣呢?因?yàn)?pointer 指向的空間是??臻g,??臻g在 g() 函數(shù)返回之后,活動(dòng)記錄就被釋放了。被釋放后調(diào)用 printf 函數(shù),printf 函數(shù)需要在棧上面建立一個(gè)活動(dòng)記錄。這個(gè)活動(dòng)記錄就會(huì)有 printf 函數(shù)的參數(shù)信息和返回值等,所以 pointer 所指向的內(nèi)存里面的數(shù)據(jù)由于 printf 函數(shù)的調(diào)用被改變了。因此,不能返回局部變量的地址,不能返回局部數(shù)組的數(shù)組名。
五、程序中的堆
- 堆是程序中一塊預(yù)留的內(nèi)存空間,可由程序自由使用
- 堆中被程序申請(qǐng)使用的內(nèi)存在被主動(dòng)釋放前將一直有效
為什么有了棧還需要堆?
答:棧上的數(shù)據(jù)在函數(shù)返回后就會(huì)被釋放掉,無(wú)法傳遞到函數(shù)外部,如:局部數(shù)組
C語(yǔ)言程序中通過(guò)庫(kù)函數(shù)的調(diào)用獲得堆空間
- 頭文件:malloc.h
- malloc -- 以字節(jié)的方式動(dòng)態(tài)申請(qǐng)堆空間
- free -- 將堆空間歸還給系統(tǒng)
系統(tǒng)對(duì)堆空間的管理方式
空閑鏈表法,位圖法,對(duì)象池法等等

以 int* p = (int*)malloc(sizeof(int)); 為例,要申請(qǐng) 4 個(gè)字節(jié)的大小,遍歷之后發(fā)現(xiàn)跟 5 Bytes 這個(gè)節(jié)點(diǎn)最接近,找到一個(gè)可以用的單元之后,就將這個(gè)單元的地址返還給了 p 指針。以前也提過(guò)使用 malloc 申請(qǐng)內(nèi)存空間時(shí)返回的內(nèi)存空間可能比申請(qǐng)的實(shí)際內(nèi)存空間要大一點(diǎn)點(diǎn),原因就是在空閑鏈表管理堆空間這樣的系統(tǒng)里面,它會(huì)找最近的那個(gè),找到后的一般都大于等于所需要的內(nèi)存空間,假如 5 Bytes 這個(gè)節(jié)點(diǎn)下所有的空閑內(nèi)存單元都用完的話,就會(huì)找 12 Bytes 節(jié)點(diǎn)下的內(nèi)存單元,這樣malloc 返回的內(nèi)存空間就有可能比自己實(shí)際申請(qǐng)的內(nèi)存空間要大。
六、程序中的靜態(tài)存儲(chǔ)區(qū)
- 靜態(tài)存儲(chǔ)區(qū)隨著程序的運(yùn)行而分配空間
- 靜態(tài)存儲(chǔ)區(qū)的生命周期直到程序運(yùn)行結(jié)束
- 在程序的編譯期靜態(tài)存儲(chǔ)區(qū)的大小就已經(jīng)確定
- 靜態(tài)存儲(chǔ)區(qū)主要用于保存全局變量和靜態(tài)局部變量
- 靜態(tài)存儲(chǔ)區(qū)的信息最終會(huì)保存到可執(zhí)行程序中
下面看一個(gè)靜態(tài)存儲(chǔ)區(qū)的驗(yàn)證代碼:
#include <stdio.h>
int g_v = 1;
static int g_vs = 2;
void f()
{
static int g_vl = 3;
printf("%p\n", &g_vl);
}
int main()
{
printf("%p\n", &g_v);
printf("%p\n", &g_vs);
f();
return 0;
}輸出結(jié)果如下:

可以看到這三個(gè)地址是順序存放的,因?yàn)檫@三個(gè)變量都是存放在程序的靜態(tài)存儲(chǔ)區(qū),靜態(tài)存儲(chǔ)區(qū)在程序里面有固定的起始地址。
七、小結(jié)
棧,堆和靜態(tài)存儲(chǔ)區(qū)是程序中的三個(gè)基本數(shù)據(jù)區(qū)
- 棧區(qū)主要用于函數(shù)調(diào)用的使用
- 堆區(qū)主要是用于內(nèi)存的動(dòng)態(tài)申請(qǐng)和歸還
- 靜態(tài)存儲(chǔ)區(qū)用于保存全局變量和靜態(tài)變量
到此這篇關(guān)于C語(yǔ)言深入講解棧與堆和靜態(tài)存儲(chǔ)區(qū)的使用的文章就介紹到這了,更多相關(guān)C語(yǔ)言 棧與堆內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)基于控制臺(tái)的電子時(shí)鐘
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)基于控制臺(tái)的電子時(shí)鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
C語(yǔ)言修煉之路靈根孕育源流出?初識(shí)C言大道生下篇
C語(yǔ)言是一門(mén)面向過(guò)程、抽象化的通用程序設(shè)計(jì)語(yǔ)言,廣泛應(yīng)用于底層開(kāi)發(fā)。C語(yǔ)言能以簡(jiǎn)易的方式編譯、處理低級(jí)存儲(chǔ)器。C語(yǔ)言是僅產(chǎn)生少量的機(jī)器語(yǔ)言以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的高效率程序設(shè)計(jì)語(yǔ)言2022-03-03
解決Visual?Studio?Code錯(cuò)誤Cannot?build?and?debug?because?
這篇文章主要為大家介紹了解決Visual?Studio?Code錯(cuò)誤Cannot?build?and?debug?because?the及分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
C語(yǔ)言中strcpy()函數(shù)的具體實(shí)現(xiàn)及注意事項(xiàng)
C語(yǔ)言庫(kù)函數(shù)char *strcpy(char *dest, const char *src)把src所指向的字符串復(fù)制到dest,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中strcpy()函數(shù)的具體實(shí)現(xiàn)及注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-11-11
C++ vector在多線程操作中出現(xiàn)內(nèi)存錯(cuò)誤問(wèn)題及解決
這篇文章主要介紹了C++ vector在多線程操作中出現(xiàn)內(nèi)存錯(cuò)誤問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
c++中的volatile和variant關(guān)鍵字詳解
大家好,本篇文章主要講的是c++中的volatile和variant關(guān)鍵字詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01

