網站首頁 編程語言 正文
地址空間
首先我們回味一下之前的老圖,這個圖由于是我手殘加 ppt 即時創作,又因為是C語言入門時講的,內容非常粗糙磕磣。要仔細研究這張圖我們應該將它翻轉90度會更加容易理解更貼近原理:
我們所熟知的,棧區數據存儲的地址是從高地址到低地址,堆區數據存儲的地址則是由低到高,而堆區下面可細分為未初始化和已初始化的全局數據區,字符常量區和代碼區。而細心的你可能注意到了我代碼區下面留了一撮空間代表下面還有,但這一撮屬于灰色地帶,目前看作為 “內存” ,但本質上不是內存,涉及到計算機操作系統原理不贅述。
從內存中0x000……0到堆區的地方其實基本上伴隨整個程序的運行一直都存在,我們相對熟悉的就是棧區和堆區,棧區的我們函數調用后臨時變量在棧幀中形成,隨著申請與釋放來進行空間管理。
那伴隨整個程序的運行一直都存在的這部分數據,像 static 這類函數修飾的變量,為什么又會被改變生命周期呢?其實在編譯的時候就被編譯進了全局數據區。
指針與內存關系
void function(char *a) { return 1; }
我們在寫函數時如果內容傳的是指針,如果有好的習慣一般會先去對指針做一下合法性判定,這個判定什么意思,比如我們傳了一個野指針,它會指向內存中任何一個位置,我是沒有辦法確認這個隨機位置有無訪問權限,所以要做合法性判定。
但是指針如果有具體的指向,對應的合法性我們是沒辦法驗證的,包括野指針,是不是很疑惑,野指針不是隨機指向,說白了就是亂指,那還沒辦法驗證嗎?是的,沒辦法。很簡單,確認指針具體值的合法性,這不是咱作為用戶可以做到的,這屬于操作系統職責。
這種尷尬的情況我們所謂的合法性判定怎么搞呢?我們所謂的“合法”是落足于應用層面。其實所有的指針在沒有被使用時,我們都應該設置成 NULL,這是一個規范問題。
在函數內部要驗證指針合法性時,本質上就是在驗證指針( !=NULL)。可以直接 if 判斷,還有就是很多書中用的一個檢查指針的宏——assert ,一般是在調試階段使用,assert(name)如果內部條件不滿足非空,就會直接咔嚓掉,中道崩殂沒有后續。但是不好意思,assert只能檢驗是否 NULL,不能檢驗是否為野指針。
內存分配與初始化細節
之前就想專門提一下幾個和內存空間有關聯的函數,現在就放在這里一起總結了吧。
我們為指針分配了內存,但是內存大小多少會影響實際結果,不夠就會造成越界。
char *p = "hello"; char *q = (char*)malloc(sizeof(char)*strlen(p)+1*sizeof(char)); strcpy(q,p);
p是字符串變量,長度為 5 個字符,但實際內存占用 6 個字符,不要忘了" \0 ",所以我們做 +1 處理,分配完了記得要初始化,初始化為非必須操作,但建議初始化,這是為了能讓咱編碼盤的明明白白。我們初始化變量時直接 0 或者 NULL,數組可以 = {0},也可以使用 memset 函數:
memset(a,0,sizeof(a));
它的 3 個參數分別代表起始地址,初始化設置的值以及設置的內存大小,單位為字節。
內存泄漏
int main() { while(1) { int *p = malloc(1024); } }
這里不做測試了,這會讓電腦越來越卡,死循環加申請空間,程序級別的老賴,空間只借不還,以上代碼就生動詮釋了何為內存泄漏。
給個C語言之外的問題:程序掛了,已經退出了,那內存泄露問題還在嗎?我自己的想法是在的,因為內存已經被申請了,退出程序只是終止了空間繼續申請,不影響已產生的空間。但是我錯了,其實在程序退出時,操作系統會強制拿回這部分空間,內存泄漏也就不在了。
所以諸位警惕windows的操作系統和殺毒軟件這類常駐進程,幾乎從來不會退出,最怕的就是內存泄漏,藍屏安排,卡頓安排;但后端的服務器也是如此,無時無刻提供服務,一但內存泄露就會嘿嘿。
Cookie
malloc 之后空間要給 free 掉,我們 free(p)目前只知道堆空間的起始地址,并不知道要釋放多少空間,如果 p 是 5 個字節,那么 free 一定會釋放的比5個字節多,那么辯證思維,其實申請的空間就一定會比 5 個字節多。
編譯器是怎么做到正確釋放呢?其實實際 malloc 申請空間的時候,系統就會給的更多,多出來的部分,記錄的就是申請的詳細信息:空間大小,申請時間等等,free 會確認信息然后精準 free掉。
這部分多申請的空間叫 cookie,內存級的 cookie,就是用來保存這些信息的。再延伸就是C語言的邊界操作系統了,不贅述。
所以我們在 malloc 時肯定是申請大空間會更好,因為 cookie 的比例會更小,想象一下利息相同時你會借多借少就能體會了。
原文鏈接:https://blog.csdn.net/qq_61500888/article/details/122170203
相關推薦
- 2022-02-12 Xpath中text()方法獲取列表為空問題解決方法
- 2022-06-25 Android開發實現圖片大小與質量壓縮及保存_Android
- 2023-01-18 你不知道的C++中namespace和using的用法實例_C 語言
- 2023-06-03 React實現一個倒計時hook組件實戰示例_React
- 2022-12-22 python實現將list拼接為一個字符串_python
- 2022-07-14 關于C#?dynamic裝箱問題_C#教程
- 2022-10-16 Ant?Design?組件庫按鈕實現示例詳解_React
- 2022-12-09 Flask自定義序列化超詳細講解_python
- 最近更新
-
- 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同步修改后的遠程分支