網站首頁 編程語言 正文
malloc
void *malloc( size_t?size?);
Tips:這里的size代表的是字節的大小
malloc的使用:
//malloc的使用 #define _CRT_SECURE_NO_WARNINGS 1 #include#include #include #include int main() { int* str = 0; int* p = 0; str = (int*)malloc(10*sizeof(int));//開辟十個整型空間 if (NULL == str) { printf("%s\n", strerror(errno));//若開辟失敗 //使用報錯函數strerror(errno) 要引用頭文件 } else { p = str; } free(p); p = NULL; return 0; }
free
釋放申請的內存空間,例:free(p)
當釋放后,雖然p中的值還在,不變,但p就為野指針了。所以建議釋放后將p設置為空指針。(p=NULL)
calloc
calloc:開辟并且初始化為0的數組。
void* calloc(size_t num,size_t size)
- num——元素個數
- size——元素大小
成功的話返回地址,失敗返回空指針NULL
calloc的使用:
#define _CRT_SECURE_NO_WARNINGS 1 #include#include #include #include int main() { int* str = 0; int* p = 0; str = (int*)calloc(10,sizeof(int)); if (NULL == str) { printf("%s\n", strerror(errno)); } else { p = str; } free(p); p = NULL; return 0; }
realloc
可開辟空間,也可以調整空間。
void?*realloc(?void?*memblock,?size_t?size?);
- memblock——要開辟空間的指針類型
- size——要開辟的字節大小
p=(int)realloc(p,80)*
——這樣子寫也是有風險的。
風險:為了避免可能會把增容的后面的已有的內存空間給覆蓋掉,所以會在另一塊大小足夠的地方開辟空間,然后把原來的數據轉移到新的空間上。并且把原來的內存空間給釋放掉。
若realloc調整空間失敗,則返回NULL。原來的數據也沒有了。
realloc的使用改進:
int* ptr=(int*)realloc(p,80); if(NULL!=ptr) { p=ptr;//這樣子能夠保證確定了不為空指針后才正式傳給p,相當于沒有了會失去原來數據的風險 }
realloc的另一種用法:
int* p=(int*)realloc(NULL,40);
這種寫法相當于malloc
常見的動態內存錯誤
對空指針的解引用操作
將malloc函數開辟一個賊大的空間,INT_MAX,此時會有一個空指針,進行判斷,如果為空指針就立馬結束這個程序了。不要出問題(ps:這里的INT_MAX的使用要引用頭文件limits.h)
所以要判斷是不是空指針,是的話就中斷,例:
//錯誤寫法 #define _CRT_SECURE_NO_WARNINGS 1 #include#include #include #include #include int main() { int i = 0; int* p = (int*)malloc(INT_MAX); for (i = 0; i < 5; i++) { *(p + i) = i; } return 0; } //正確寫法: #define _CRT_SECURE_NO_WARNINGS 1 #include #include #include #include #include int main() { int i = 0; int* p = (int*)malloc(INT_MAX); if (p == NULL) { printf("%s\n", strerror(errno));//這里是將錯誤報出來 return 0;//發現是空指針,提前結束 } for (i = 0; i < 5; i++) { *(p + i) = i; } return 0; }
對動態開辟空間的越界訪問
不可以不申請即使用動態內存空間,會報錯的。
Tips:沒有開辟的空間是不能使用的
對非動態開辟內存使用free釋放
int main() { int p=0; int* a=&p; free(a);//這個樣子是錯誤的 return 0; }
使用free釋放一塊動態開辟內存的一部分
開辟動態空間的時候,一定要把起始位置給用變量存好,否則到時會無法釋放內存。
//使用free釋放一塊動態開辟內存的一部分 //正確寫法: #define _CRT_SECURE_NO_WARNINGS 1 #include#include int main() { int i = 0; int* p = (int*)malloc(10 * sizeof(int)); //正確寫法: for (i = 0; i < 5; i++) { *(p + i) = i; } free(p); p=NULL; return 0; } //錯誤寫法: #define _CRT_SECURE_NO_WARNINGS 1 #include #include int main() { int i = 0; int* p = (int*)malloc(10 * sizeof(int)); //錯誤寫法: for (i = 0; i < 5; i++) { *p = i; p++;//這里會改變p的原始位置,使得無法指向一開始開辟動態內存空間的位置,最終報錯 } free(p); p = NULL; return 0; }
對同一塊動態內存多次釋放
一塊空間釋放后不可再釋放,但釋放完后p置為空指針再次釋放時不會報錯。
Q:free空指針時會有問題么?
A:不會,因為一塊空間釋放后就不能再次釋放了,所以每次free完后記得置為空指針。
動態開辟內存忘記釋放(內存泄露)
即使在函數中開辟內存空間也要記得釋放。因為出了函數在外面想釋放也無法釋放。
但如果返回首元素的地址,free了也行,就是無論怎么樣,一定要釋放。
在任何地方開辟的內存空間都最好要釋放。
找出下面問題:
T1:
void GetMemory(char* p) { p=(char*)malloc(100); } void Test(void) { char* str=NULL; GetMemory(str); strcpy(str,"hello world"); printf(str); } int main() { Test(); return 0; }
出現的問題:
在這里str是空指針,而p只是新建的一個形參,運行完函數后無法返回p不存在了,但是內存空間還未被釋放,而這個空間的地址此時是沒有人能夠知道的。也并不能將str里面的NULL改變,所以在strcpy時會出錯,因為str此時為NULL指針,會造成非法訪問內存,程序會崩潰。
而且在使用過程中只進行了動態內存的開辟,沒有進行動態內存的釋放,可能會造成動態內存泄露。
改進方法:
#define _CRT_SECURE_NO_WARNINGS 1 #include#include #include char* GetMemory(char* p) { p = (char*)malloc(100); return p; } void Test(void) { char* str = NULL; str = GetMemory(str); strcpy(str, "hello world"); printf(str); free(str); str = NULL; } int main() { Test(); return 0; }
函數的棧幀與創建:p盡管銷毀,因為會先把p里面的值放入到寄存器中,寄存器里面不會銷毀,之后再從寄存器位置傳進去str。
T2:
出現的問題:
返回棧空間地址問題:
這里雖然能把p的地址傳回去,但是在函數運行完后在函數里面創建的數據會被銷毀,也就是說雖然能通過指針找到原來的內存所指向的地方,但是數據都以被銷毀。
注意!!!
這樣是可以的,因為返回的是棧空間的變量而不是棧空間的地址。
總結:
在創造函數如果返回地址而不是返回值,在用的時候可能依然是在函數內的值,但也有很大可能不是,可能不是的原因是有關函數棧幀方面,如果在引用地址前再寫上一段例如:"printf("23333\n");",可能會導致覆蓋掉原來地址上的數據,所以無法通過傳址來輸出真正的值,因為會被覆蓋掉。
T3:
出現的問題:
除了free沒有太大毛病了。這里能夠打印出hello。
T4:
出現的問題:
這里的free其實是把動態內存空間還給系統了,但是str的話沒有定為空指針,仍然存著當初指向開辟的內存空間的地址,那么就還可以通過str找到當初開辟的內存空間,只是這個時候因為釋放(free)str了,所以此時沒有訪問空間的權限,也就無法將world拷貝到str所指向的空間。
正確改法:
所以,在每次free后面都要記得設置為空指針。
柔性數組
在c99中,結構體中的最后一個元素是允許未知大小的數組,這就叫做【柔性數組】成員。
柔性數組的定義
//寫法一: struct s1 { int n; int arr[0];//大小是未指定 } //寫法二: struct s2 { int n; int arr[];//大小是未指定 } //總會有一種寫法編譯器不報錯
Tips:在計算包含柔性數組大小的時候,柔性數組是不計算在大小里面的。(可以寫一個來試一下)
柔性數組的特點:
- 柔性數組前至少需要一個其他成員
- sizeof返回的這種結構大小不包括柔性數組的內存
- 包含柔性數組成員的結構用malloc()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
柔性數組的開辟(自己先寫)
包含柔性數組的結構體不可以直接創建,而是要有malloc來開辟空間。
//寫柔性數組的方法一: #define _CRT_SECURE_NO_WARNINGS 1 #include#include #include struct p { int i; int arr[]; }; int main() { struct p* cmp = (struct p*)malloc(sizeof(struct p) + 80);//這里是開辟了一共84個字節空間,分給arr數組80個字節空間 free(p); p = NULL; return 0; } //寫柔性數組的方法二:(先開辟整個的,再開辟數組的) #define _CRT_SECURE_NO_WARNINGS 1 #include #include #include struct p { int i; int* arr;//這樣才能在方法二中使用 }; int main() { struct p* cmp = (struct p*)malloc(sizeof(struct p)); cmp->i = 10; cmp->arr = (int*)malloc(80);//從數組開始,再次開辟80個字節空間 free(p); p = NULL; return 0; }
第二種方案(劣勢):
1.開辟和釋放的次數多,容易出錯
2.頻繁多次開辟內存,會有內存碎片出現,可能會導致內存的使用效率不高
第一種方案優勢:
1.方便釋放
2.減少內存碎片的出現
總結
原文鏈接:https://blog.csdn.net/Green_756/article/details/123595906
相關推薦
- 2023-02-01 MongoDB?事務支持詳解_MongoDB
- 2021-12-09 JQuery選擇器詳解_jquery
- 2022-06-01 python中使用正則表達式的方法詳解_python
- 2022-07-15 Qt各種字符轉換的實現示例_C 語言
- 2022-06-17 Android自定義實現可回彈的ScollView_Android
- 2022-07-26 ubuntu18.04+cuda10.2+tensorrt8.4.1.5配置安裝
- 2022-08-30 MQTT - 消息隊列遙測傳輸協議
- 2022-05-11 如何免安裝服務器將 React 整合進 Spring Boot
- 最近更新
-
- 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同步修改后的遠程分支