C語言 動(dòng)態(tài)內(nèi)存開辟常見問題解決與分析流程
前言
當(dāng)我們用動(dòng)態(tài)內(nèi)存分配函數(shù)來編寫程序時(shí),在編寫的過程中常常會(huì)產(chǎn)生一些不易被察覺,被發(fā)現(xiàn)的錯(cuò)誤,例如對(duì)NULL指針的解引用操作,對(duì)動(dòng)態(tài)開辟空間的越界訪問,使用free釋放非動(dòng)態(tài)開辟的空間,使用free釋放動(dòng)態(tài)內(nèi)存中的一部分,對(duì)同一塊動(dòng)態(tài)開辟的空間,多次釋放,動(dòng)態(tài)開辟空間忘記釋放。下面我們挨個(gè)來分析,刨析一下這些個(gè)常見的動(dòng)態(tài)內(nèi)存開辟的問題。
一、動(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)開辟空間的越界訪問
代碼如下(示例):
錯(cuò)誤示例:
//動(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 < 40; i++)//malloc函數(shù)只是開辟了十個(gè)整型的空間,這里卻要訪問四十個(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)申請空間的地址,存放在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í)際意義的,如果通過返回的地址,去訪問內(nèi)存就是非法訪問內(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ò)誤分析:
申請的動(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.非法訪問內(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ò)誤分析:
申請的空間已經(jīng)free釋放還給操作系統(tǒng)了,及時(shí)str還記得這塊空間的起始地址,但是也不能訪問,屬于非法訪問內(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é):
上述給大家簡單介紹了動(dòng)態(tài)內(nèi)存開辟常見的幾種問題,也分析了往年的幾道面試題里面的錯(cuò)誤,讓我們加深了對(duì)這一章的理解,后續(xù)自己使用的時(shí)候可以有效的規(guī)避掉這些問題。相信大家都學(xué)會(huì)了。如果上述文章有任何問題?,歡迎大佬們提出質(zhì)疑,我會(huì)虛心學(xué)習(xí)和改正,最重要的是能共同進(jìn)步,共同成長,學(xué)習(xí)好編程。
到此這篇關(guān)于C語言 動(dòng)態(tài)內(nèi)存開辟常見問題解決與分析流程的文章就介紹到這了,更多相關(guān)C語言 動(dòng)態(tài)內(nèi)存開辟內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C語言編程動(dòng)態(tài)內(nèi)存開辟實(shí)現(xiàn)升級(jí)版通訊錄教程示例
- 詳細(xì)談?wù)凜語言中動(dòng)態(tài)內(nèi)存
- C語言?動(dòng)態(tài)內(nèi)存管理全面解析
- C語言的動(dòng)態(tài)內(nèi)存管理的深入了解
- 詳解C語言動(dòng)態(tài)內(nèi)存的分配
- C語言動(dòng)態(tài)內(nèi)存管理介紹
- C語言動(dòng)態(tài)內(nèi)存管理分析總結(jié)
- 詳解C語言之動(dòng)態(tài)內(nèi)存管理
- C語言編程動(dòng)態(tài)內(nèi)存分配常見錯(cuò)誤全面分析
相關(guān)文章
QT設(shè)置widget背景圖片不影響widget內(nèi)其他控件背景的方法
這篇文章主要給大家介紹了關(guān)于QT設(shè)置widget背景圖片不影響widget內(nèi)其他控件背景的方法,軟件的界面為了更直觀或美觀,常常需要通過圖片來表達(dá),需要的朋友可以參考下2023-06-06
使用MySQL編程實(shí)現(xiàn)C語言功能強(qiáng)大化步驟示例
這篇文章主要為大家介紹了使用MySQL編程實(shí)現(xiàn)C語言功能強(qiáng)大化步驟示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
C++語言基礎(chǔ) this和static關(guān)鍵字
這篇文章主要介紹了C++語言基礎(chǔ) this和static關(guān)鍵字,需要的朋友可以參考下2020-01-01
C++工廠方法之對(duì)象創(chuàng)建型模式詳解
這篇文章主要為大家詳細(xì)介紹了C++對(duì)象創(chuàng)建型模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
C語言結(jié)構(gòu)及隊(duì)列實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了C語言實(shí)現(xiàn)隊(duì)列示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
形參出現(xiàn)在函數(shù)定義中,在整個(gè)函數(shù)體內(nèi)都可以使用, 離開該函數(shù)則不能使用。實(shí)參出現(xiàn)在主調(diào)函數(shù)中,進(jìn)入被調(diào)函數(shù)后,實(shí)參變量也不能使用,形參和實(shí)參的功能是作數(shù)據(jù)傳送。發(fā)生函數(shù)調(diào)用時(shí), 主調(diào)函數(shù)把實(shí)參的值傳送給被調(diào)函數(shù)的形參從而實(shí)現(xiàn)主調(diào)函數(shù)向被調(diào)函數(shù)的數(shù)據(jù)傳送2023-02-02

