網站首頁 編程語言 正文
1.為什么需要動態內存分配
關于這個問題,我們先看看我們之前是如何開辟內存的。
int val = 20;//在棧空間上開辟四個字節 char arr[10] = {0};//在棧空間上開辟10個字節的連續空間
但可以發現的一個問題是,無論我們怎樣開辟內存空間,他的大小都在開辟前就已經被指定,而顯然在實際應用中并不是所有情況我們都能在程序編譯前就知道他需要多大的內存空間。或許你想說那有備無患開大點不久好了?但這樣所造成的空間浪費并不是我們所希望看到的結果。于是我們就只能試試動態內存分配了。
2.有關動態內存函數介紹
2.1 malloc和free
c語言已經為我們提供了一個動態內存開辟的函數malloc
void* malloc (size_t size);
1.這個函數向內存申請一塊連續可用的空間,并返回指向這塊空間的指針。
2.如果開辟成功,則返回一個指向開辟好空間的指針。
3.如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。
4.返回值的類型是 void* ,所以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自 己來決定。
5.如果參數 size 為0,malloc的行為是標準是未定義的,取決于編譯器。
當然有分配就會有釋放,c語言也為我們提供了另一個函數free,專門用來做動態內存的釋放以及回收,函數原型如下。
void free (void* ptr);
1.free函數用來釋放動態開辟的內存。
2.如果參數 ptr 指向的空間不是動態開辟的,那free函數的行為是未定義的。
3.如果參數 ptr 是NULL指針,則函數什么事都不做。
我們看到下面的一個例子
#include <stdio.h> int main() { int* ptr = NULL; ptr = (int*)malloc(num * sizeof(int)); if (NULL != ptr)//判斷ptr指針是否為空 { int i = 0; for (i = 0; i < num; i++) { *(ptr + i) = 0; } } free(ptr);//釋放ptr所指向的動態內存 ptr = NULL; return 0; }
相信這串代碼大家在有注釋的情況下都能夠看懂。但是有細心的讀者可能會發現free(ptr)之后又令ptr=NULL,這是為什么呢?其實在一塊內存空間被釋放后,該指針仍指向被釋放掉的內存地址,而此時的ptr便成了野指針,一旦后續不小心被程序調用就會導致程序崩潰,所以在指針釋放后要將其置為NULL防止這種情況的發生。
2.2 calloc函數
calloc函數類似與malloc,同樣用于動態內存分配,函數原型如下。
void* calloc (size_t num, size_t size);
函數的功能是為 num個大小為size的元素開辟一塊空間,并且把空間的每個字節初始化為0。
與函數malloc的區別只在于calloc會在返回地址之前把申請的空間的每個字節初始化為全0。
2.3 realloc函數
有時會我們發現過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那為了合理的時 候內存,我們一定會對內存的大小做靈活的調整。那 realloc 函數就可以做到對動態開辟內存大 小的調整。函數原型如下。
void* realloc (void* ptr, size_t size);
ptr 是要調整的內存地址
size 調整之后新大小
返回值為調整之后的內存起始位置。
這個函數調整原內存空間大小的基礎上,還會將原來內存中的數據移動到新的空間。
需要注意的是:
realloc在調整內存空間時存在兩種情況
情況1:原有空間之后有足夠大的空間
情況2:原有空間之后沒有足夠大的空間
參見下圖示意
?情況1
當是情況1的時候,要擴展內存就直接原有內存之后直接追加空間,原來空間的數據不發生變化。
情況2
當是情況2 的時候,原有空間之后沒有足夠多的空間時,擴展的方法是:在堆空間上另找一個合適大小 的連續空間來使用。這樣函數返回的是一個新的內存地址。
針對情況二,若realloc成功,指向原內存地址的指針就成了懸掛指針,即指針指向了一塊沒有分配給用戶使用的內存,如果再使用該指針進行操作就可能發生意想不到的情況,因此要格外注意這種情況。
#include <stdio.h> int main() { int* ptr = (int*)malloc(100);//動態分配 int* p = NULL; p = (int*)realloc(ptr, 1000);//重分配 if (p != NULL)//判斷是否成功 { ptr = p;//防止懸掛指針出現 } //業務處理 free(ptr); ptr=NULL;//釋放后置空防止野指針 return 0; }
注意動態內存分配時應當像上述代碼一樣盡量規范。
3. 常見的動態內存錯誤
3.1 對NULL指針進行解引用操作
這個點就不再過多敘述,大家記住即可,對空指針進行解引用操作可能會引發各種奇怪的問題。
3.2?對動態開辟空間的越界訪問
同數組類似,即使是動態開辟的空間也不能越界訪問。
3.3 對非動態開辟內存使用free釋放
切記只有動態開辟的內存才能使用free。
3.4 使用free釋放一塊動態開辟內存的一部分
參見下面代碼
void test() { int *p = (int *)malloc(100); p++; free(p);//p不再指向動態內存的起始位置 }
雖然有些小伙伴可能想既然free函數是根據指針來釋放內存的,那我能不能通過對指針進行操作去部分釋放動態分配的內存呢?然而夢想很美好,現實很骨感。如果強行這樣做的話只可能會造成·更多不可預估的結果。
3.5 對同一塊動態內存多次釋放
這個錯誤應該大家目前應該不太常犯,但是一旦后面代碼量大了之后就很有可能忘記是否已經釋放過內存從而導致重復釋放而bug。
3.6 動態開辟內存忘記釋放(內存泄漏)
void test() { int *p = (int *)malloc(100); if(NULL != p) { *p = 20; } } int main() { test(); while(1); }
看到這個標題再看這串代碼,大家應該都很容易能夠知道上面的代碼忘記釋放內存了從而導致內存泄漏,但實際日常我們非常容易忘記開辟內存后free。
忘記釋放不再使用的動態開辟的空間會造成內存泄漏。
切記:
動態開辟的空間一定要釋放,并且正確釋放 。
總結
原文鏈接:https://blog.csdn.net/weixin_60778429/article/details/122698675
相關推薦
- 2022-06-02 Docker部署項目完全使用指南(小結)_docker
- 2022-09-13 Python利用臨時文件實現數據的保存_python
- 2023-05-21 C#?Replace替換的具體使用_C#教程
- 2022-10-22 Golang?中反射的應用實例詳解_Golang
- 2023-07-10 NGINX使用rewrite報錯。
- 2022-07-12 Docker-swarm快速搭建redis集群的方法步驟_docker
- 2022-05-13 Missing essential plugin: org.jetbrains.androidPle
- 2022-11-24 Python模板的使用詳細講解_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同步修改后的遠程分支