網站首頁 編程語言 正文
一、程序中的棧
- 棧是現代計算機程序里最為重要的概念之一
- 棧在程序中用于維護函數調用上下文
- 函數中的參數和局部變量存儲在棧上
?棧保存了一個函數調用所需的維護信息
- 參數
- 返回地址
- 局部變量
- 調用上下文
二、函數的調用過程
每次函數調用都對應著一個棧上的活動記錄
- 調用函數的活動記錄位于棧的中部
- 被調函數的活動記錄位于棧的頂部
三、函數調用的棧變化
從main() 開始運行
?main() 調用 f()
?當從 f() 調用中返回 main()
四、函數調用棧上的數據
- 函數調用時,對應的棧空間在函數返回前是專用的
- 函數調用結束后,棧空間將被釋放,數據不再有效
? ? ? ? 下面看一個指向棧數據的指針:?
#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;
}
? ? ? ? ?輸出結果如下:
? ? ? ? 如果把?
????????for (i = 0; i < 10; i++)
? ?????{
? ? ? ????????? b[i] = pointer[i];
? ?????}
????????注釋了,直接打印 pointer[i] 里面的數據,如下:
#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;
}
? ? ? ? 輸出結果如下:
? ? ? ? ?為什么直接打印 pointer[i] 里面的值會是這樣呢?因為 pointer 指向的空間是棧空間,棧空間在 g() 函數返回之后,活動記錄就被釋放了。被釋放后調用 printf 函數,printf 函數需要在棧上面建立一個活動記錄。這個活動記錄就會有 printf 函數的參數信息和返回值等,所以 pointer 所指向的內存里面的數據由于 printf 函數的調用被改變了。因此,不能返回局部變量的地址,不能返回局部數組的數組名。
五、程序中的堆
- 堆是程序中一塊預留的內存空間,可由程序自由使用
- 堆中被程序申請使用的內存在被主動釋放前將一直有效
????????為什么有了棧還需要堆?
? ? ? ? 答:棧上的數據在函數返回后就會被釋放掉,無法傳遞到函數外部,如:局部數組
C語言程序中通過庫函數的調用獲得堆空間
- 頭文件:malloc.h
- malloc -- 以字節的方式動態申請堆空間
- free -- 將堆空間歸還給系統
系統對堆空間的管理方式
空閑鏈表法,位圖法,對象池法等等
? ? ? ? 以?int* p = (int*)malloc(sizeof(int)); 為例,要申請 4 個字節的大小,遍歷之后發現跟 5 Bytes 這個節點最接近,找到一個可以用的單元之后,就將這個單元的地址返還給了 p 指針。以前也提過使用 malloc 申請內存空間時返回的內存空間可能比申請的實際內存空間要大一點點,原因就是在空閑鏈表管理堆空間這樣的系統里面,它會找最近的那個,找到后的一般都大于等于所需要的內存空間,假如?5 Bytes 這個節點下所有的空閑內存單元都用完的話,就會找?12 Bytes 節點下的內存單元,這樣malloc 返回的內存空間就有可能比自己實際申請的內存空間要大。
六、程序中的靜態存儲區
- 靜態存儲區隨著程序的運行而分配空間
- 靜態存儲區的生命周期直到程序運行結束
- 在程序的編譯期靜態存儲區的大小就已經確定
- 靜態存儲區主要用于保存全局變量和靜態局部變量
- 靜態存儲區的信息最終會保存到可執行程序中
? ? ? ? 下面看一個靜態存儲區的驗證代碼:
#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;
}
? ? ? ? 輸出結果如下:
? ? ? ? ?可以看到這三個地址是順序存放的,因為這三個變量都是存放在程序的靜態存儲區,靜態存儲區在程序里面有固定的起始地址。?
七、小結
棧,堆和靜態存儲區是程序中的三個基本數據區
- 棧區主要用于函數調用的使用
- 堆區主要是用于內存的動態申請和歸還
- 靜態存儲區用于保存全局變量和靜態變量
原文鏈接:https://blog.csdn.net/weixin_43129713/article/details/124093704
相關推薦
- 2022-09-28 使用C語言實現三子棋小游戲_C 語言
- 2022-06-18 Elasticsearch之基本查詢及組合查詢操作示例_其它綜合
- 2022-06-21 C語言實現順序表的全操作詳解_C 語言
- 2022-11-17 Python中的優先隊列(priority?queue)和堆(heap)_python
- 2022-12-29 一文詳細談談GoLang的panic和error_Golang
- 2023-07-28 el-tab 切換時添加動畫
- 2022-03-27 C++浮點數在內存中的存儲詳解_C 語言
- 2022-11-12 docker修改容器內存大小的實現方式_docker
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支