網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
C語(yǔ)言?動(dòng)態(tài)內(nèi)存開辟常見問(wèn)題解決與分析流程_C 語(yǔ)言
作者:K穩(wěn)重 ? 更新時(shí)間: 2022-05-18 編程語(yǔ)言前言
當(dāng)我們用動(dòng)態(tài)內(nèi)存分配函數(shù)來(lái)編寫程序時(shí),在編寫的過(guò)程中常常會(huì)產(chǎn)生一些不易被察覺,被發(fā)現(xiàn)的錯(cuò)誤,例如對(duì)NULL指針的解引用操作,對(duì)動(dòng)態(tài)開辟空間的越界訪問(wèn),使用free釋放非動(dòng)態(tài)開辟的空間,使用free釋放動(dòng)態(tài)內(nèi)存中的一部分,對(duì)同一塊動(dòng)態(tài)開辟的空間,多次釋放,動(dòng)態(tài)開辟空間忘記釋放。下面我們挨個(gè)來(lái)分析,刨析一下這些個(gè)常見的動(dòng)態(tài)內(nèi)存開辟的問(wèn)題。
一、動(dòng)態(tài)內(nèi)存錯(cuò)誤
1.對(duì)NULL指針的解引用操作
代碼如下(示例):
錯(cuò)誤示例: //動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = malloc(100000000000); //沒有對(duì)mollac函數(shù)的返回值做判空處理 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = 5; } return 0; } 正確示例: //動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = malloc(100000000000); if (p == NULL) { return 1; } int i = 0; for (i = 0; i < 10; i++) { *(p + i) = 5; } for (i = 0; i < 10; i++) { printf("%d ", p[i]); } free(p); p = NULL; return 0; }
2.對(duì)動(dòng)態(tài)開辟空間的越界訪問(wèn)
代碼如下(示例):
錯(cuò)誤示例: //動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = (int*)malloc(10*sizeof(int)); if (p == NULL) { return 1; } int i = 0; //越界訪問(wèn) for (i = 0; i < 40; i++)//malloc函數(shù)只是開辟了十個(gè)整型的空間,這里卻要訪問(wèn)四十個(gè)元素。 { *(p + i) = 5; } for (i = 0; i < 40; i++) { printf("%d ", p[i]); } free(p); p = NULL; return 0; } 正確示例: //動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = (int*)malloc(10*sizeof(int)); if (p == NULL) { return 1; } int i = 0; for (i = 0; i < 10; i++) *(p + i) = 5; } for (i = 0; i < 10; i++) { printf("%d ", p[i]); } free(p); p = NULL; return 0; }
3.使用free釋放非動(dòng)態(tài)開辟的空間
代碼如下(示例):
//動(dòng)態(tài)內(nèi)存開辟 int main() { int arr[10] = { 0 };//棧區(qū) int* p = arr; free(p);//使用free釋放非動(dòng)態(tài)開辟的空間 p = NULL; return 0; }
4.使用free釋放動(dòng)態(tài)內(nèi)存中的一部分
代碼如下(示例):
//動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = malloc(10 * sizeof(int)); if (p == NULL) { return 1; } int i = 0; for (i = 0; i < 5; i++) { *p++ = i;//1:p一直往后走之后沒人知道起始空間的位置在哪,2:p釋放的只是后面空間的一部分,前面的空間并沒有得到釋放。 } free(p); p = NULL; return 0; }
5.對(duì)同一塊動(dòng)態(tài)內(nèi)存動(dòng)態(tài)開辟的空間多次釋放
代碼如下(示例):
//動(dòng)態(tài)內(nèi)存開辟 int main() { int* p = malloc(10 * sizeof(int)); //使用 //釋放 free(p); //再次釋放 free(p);//free要是傳的是空指針什么事都不會(huì)發(fā)生。 p = NULL; return 0; }
6.動(dòng)態(tài)開辟的空間忘記釋放(容易造成內(nèi)存泄露,比較嚴(yán)重)
代碼如下(示例):
void test() { int* p = malloc(10 * sizeof(int)); if (p == NULL) { return 1; } //使用 //忘記釋放 } //動(dòng)態(tài)內(nèi)存開辟 int main() { test(); return 0; }
二、動(dòng)態(tài)內(nèi)存錯(cuò)誤面試題分析
1.NULL指針傳參不取地址傳的也是一份臨時(shí)拷貝
例題分析: 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; }
程序運(yùn)行結(jié)果:
拷貝不成功,程序直接掛掉。
原因分析:
str傳給GetMemory函數(shù)的時(shí)候是值傳遞,所以GetMemory函數(shù)的形參p是str的一份臨時(shí)拷貝。
在GetMemory函數(shù)內(nèi)部動(dòng)態(tài)申請(qǐng)空間的地址,存放在P中,不會(huì)影響外面str,所以當(dāng)GetMemory函數(shù)返回
之后,str任然是NULL指針,所以strcpy會(huì)失敗。
當(dāng)GetMemory函數(shù)返回之后,形參p銷毀,使得動(dòng)態(tài)開辟的100個(gè)字節(jié)存在內(nèi)存泄漏。
?正確代碼:
//第一種改法: 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; } //第二種改法: char* GetMemory(char** p) { *p = (char*)malloc(100); } void test(void) { char* str = NULL; GetMemory(&str); strcpy(str, "hello world"); printf(str); free(str); str = NULL; } int main() { test(); return 0; }
2.局部變量和形式參數(shù)存在于棧上
代碼如下(示例):
//例題分析: char* GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); } int main() { test(); return 0; }
程序運(yùn)行結(jié)果:
打印不成功,打印的都是隨機(jī)值
原因分析:
GetMemory函數(shù)內(nèi)部創(chuàng)建的數(shù)組是在棧區(qū)上創(chuàng)建的
出了函數(shù),p的數(shù)組的空間就還給了操作系統(tǒng)
返回的地址是沒有實(shí)際意義的,如果通過(guò)返回的地址,去訪問(wèn)內(nèi)存就是非法訪問(wèn)內(nèi)存。
?正確代碼:
char* GetMemory(void) { static char p[] = "hello world"; return p; } void test(void) { char* str = NULL; str = GetMemory(); printf(str); } int main() { test(); return 0; }
3.動(dòng)態(tài)內(nèi)存開的空間記得free釋放掉
代碼如下(示例)
void GetMemory(char** p, int num) { *p = (char*)malloc(num); } void Test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } int main() { test(); return 0; }
錯(cuò)誤分析:
申請(qǐng)的動(dòng)態(tài)內(nèi)存空間使用完之后沒有及時(shí)free釋放掉。
正確代碼:
void GetMemory(char** p, int num) { *p = (char*)malloc(num); } void test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); free(str); str = NULL; } int main() { test(); return 0; }
4.非法訪問(wèn)內(nèi)存
代碼如下(示例)
void test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) { strcpy(str, "world"); printf(str); } } int main() { test(); return 0; }
錯(cuò)誤分析:
申請(qǐng)的空間已經(jīng)free釋放還給操作系統(tǒng)了,及時(shí)str還記得這塊空間的起始地址,但是也不能訪問(wèn),屬于非法訪問(wèn)內(nèi)存空間。
free完之后要及時(shí)把str置成NULL指針。
正確代碼:
void test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); str = NULL; if (str != NULL) { strcpy(str, "world"); printf(str); } } int main() { test(); return 0; }
總結(jié):
上述給大家簡(jiǎn)單介紹了動(dòng)態(tài)內(nèi)存開辟常見的幾種問(wèn)題,也分析了往年的幾道面試題里面的錯(cuò)誤,讓我們加深了對(duì)這一章的理解,后續(xù)自己使用的時(shí)候可以有效的規(guī)避掉這些問(wèn)題。相信大家都學(xué)會(huì)了。如果上述文章有任何問(wèn)題?,歡迎大佬們提出質(zhì)疑,我會(huì)虛心學(xué)習(xí)和改正,最重要的是能共同進(jìn)步,共同成長(zhǎng),學(xué)習(xí)好編程。
原文鏈接:https://blog.csdn.net/m0_64397675/article/details/122942990
相關(guān)推薦
- 2022-12-16 C++?Boost?Atomic詳細(xì)講解_C 語(yǔ)言
- 2022-06-08 利用AOP+Swagger注解實(shí)現(xiàn)日志記錄功能
- 2022-05-01 jquery實(shí)現(xiàn)移動(dòng)端按鈕組左右滑動(dòng)_jquery
- 2022-09-26 快速搭建 ElasticSearch學(xué)習(xí)環(huán)境
- 2023-07-14 pinia的常用知識(shí)點(diǎn)及搭配“script setup”的使用
- 2022-06-18 kubernetes(k8s)安裝metrics-server實(shí)現(xiàn)資源使用情況監(jiān)控方式詳解_云其它
- 2022-05-26 Pytorch中使用ImageFolder讀取數(shù)據(jù)集時(shí)忽略特定文件_python
- 2022-07-12 mongoDB復(fù)雜查詢實(shí)例(嵌套多個(gè)數(shù)組和正則表達(dá)式使用)
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支