【C/C++】原來這樣做就能避免C語言懸垂指針問題

寫在前面:大家好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()函數中的變量qi是局部變量,存放在棧區中,棧區由系統分配釋放資源。

q和&p是本身,指向同一內存單元i

但是在函數fun()釋放時,變量qi也被釋放掉了。

綜上,僅剩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. 總結

  1. 局部變量在程序運行時存放在棧區,在函數釋放空間會被系統收回。此時再操作會引起程序錯誤

  2. 不要返回局部變量地址!!!

  3. 如果要使用其他函數中局部變量地址,要利用堆區!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章