寫在前面:大家好K。首先爲你點進這篇有趣的文章點贊👍!文章在撰寫過程中難免有疏漏和錯誤,歡迎你在下方留言指出文章的不足之處;如果覺得這篇文章對你有用,也歡迎你點贊和留下你的評論。更多內容請點進👉我的博客K。👈閱覽。
文章目錄
1. 內存4區
程序在運行時,指令是存放在內存中的,程序大致佔用了內存大概4個區:
代碼區、數據區、棧區、堆區
1.1 代碼區
程序指令存放在這裏,由操作系統管理
特徵:共享、且只讀
1.2 數據區
-
數據區
存放由const修飾的常量、字符串常量等
-
全局(靜態)區
存放由static修飾、全局變量等
注意#define與const不同,#define是預處理,在編譯前由編譯器簡單替換,而不是作爲常量在程序運行時放到內存
1.3 棧區(本文重點)
存放的數據由系統管理,自動分配釋放
如:函數的參數,局部變量等
1.4 堆區(本文重點)
存放的數據由開發者手動分配釋放
若開發者不釋放,則系統會在程序運行結束後回收內存
如:用malloc函數申請的內存空間
1.5 分4區的意義
賦予了不同類型的變量不同的生命週期。根據實際情況需要,選擇不同的變量聲明方式,使開發時更靈活
但是也容易帶來內存泄露等隱患,需要開發者特別注意內存管理
2. 錯誤代碼示例(懸垂指針)
// 關鍵代碼
void fun(int ** q) {
int i = 5;
*q = &i;
}
int main() {
int * p;
fun(&p);
printf("%d\n", *p);
printf("%d\n", *p);
return 0;
}
q和&p是本身(∵函數參數的地址傳遞),所以這裏沒有用return返回*q(當然也可以用)
代碼是可以運行的,運行結果是
[username@host directory]$ ./test.out
5
32766
可以看到,第一次輸出的是正確的值,但是第二次輸出了一個隨機值,這是爲什麼呢?
3. 原因(懸垂指針)
通過上面內存4區的介紹,可以知道:
fun()
函數中的變量q
和i
是局部變量,存放在棧區中,棧區由系統分配釋放資源。
q和&p是本身,指向同一內存單元i
。
但是在函數fun()
釋放時,變量q
和i
也被釋放掉了。
綜上,僅剩p
指向i
的軀殼(原對象不存在了),這裏p
就成了懸垂指針。
懸垂指針指向的內存單元已經不屬於該程序(堆區的東西被系統釋放),再操作往往會導致程序錯誤,而且難以檢測。
但是爲啥第一次輸出時是正確的值5
呢?
因爲系統會爲程序暫時保留值,但是這一次使用後就會徹底清除了,所以看到第二次取到的值是一個隨機數。
4. 如何避免
不要返回局部變量地址!!!
不要返回局部變量地址!!!
不要返回局部變量地址!!!
如果要使用別個函數中局部變量地址,記得利用堆區!!!
堆區的內存區域是由開發者手動分配釋放的,在手動釋放前可以隨意使用
4.1 C語言
利用動態內存管理的幾個函數
-
malloc()
分配指定字節數的內存區域,不初始化內容,返回首地址的void指針(可強制轉換後再使用)
// 分配連續的一段內存空間
// 大小爲400字節(這裏設int爲4字節)
// 強制轉換成int *類型的指針(同整形數組),例++p時,p指針向後移4個字節
p = (int*) malloc(100 * sizeof(int));
-
calloc()
類似
malloc()
,但會將內存清零
// 分配連續的一段內存空間
// 大小爲25個int類型大小的區域
// 強制轉換成int *類型的指針(同整形數組),例++p時,p指針向後移4個字節
p = (int*) calloc(25, sizeof(int));
-
realloc()
更改以前分配的內存的大小
// 以x的大小,形同當前p指針,重新分配內存
p = realloc(p, x);
-
free()
釋放已分配的內存區域
free(p);
// 之後再p再次被分配內存之前,不能再使用
4.2 C++
4.2.1 使用new和delete
例,使用new
爲數組申請內存空間
// 分配5個int類型元素的數組空間
// 並將前三個元素初始化11、22、33
int *p = new int[5]{11, 22, 33};
4.2.2 使用智能指針
在C++11之後的版本,包含頭文件<memory>後,可使用auto_ptr、unique_ptr、shared_ptr和weak_ptr四個智能指針
4.2.3 使用標準模板庫STL
有時爲了節省寫代碼時間,提高編程效率,和減少指針越界問題,可以使用標準模板庫STL和迭代器,用它可以輕鬆創建何使用集合set、鍵值對map、可伸縮數組vector、鏈表list、棧stacks和隊列queues等
4. 總結
-
局部變量在程序運行時存放在棧區,在函數釋放空間會被系統收回。此時再操作會引起程序錯誤
-
不要返回局部變量地址!!!
-
如果要使用其他函數中局部變量地址,要利用堆區!!!