深入了解C語言的動態(tài)內(nèi)存管理
一、為什么會存在動態(tài)內(nèi)存
int data=20;//在??臻g上開辟4個字節(jié)空間 char ch[5]={0};//在棧開辟5個字節(jié)連續(xù)空間
上面展示的即為我們正常開辟固定的內(nèi)存空間,它有兩個方面的特點
1.內(nèi)存空間所占大小是固定的,不能改變的。
2.創(chuàng)建數(shù)組時,必須指明長度大小,在編譯時內(nèi)存進(jìn)行分配。
很顯然靜態(tài)分配內(nèi)存分配在一些場景,就暴露出它的弊端。如果在開發(fā)之前,我們不知道空間的需求,我們有時只有在程序運行的時候才能知道自己所需要空間大小,這時候我們只能使用動態(tài)分配內(nèi)存了。
二、動態(tài)內(nèi)存函數(shù)
1.malloc和free
malloc函數(shù)的參數(shù)只有一個size_t size,向內(nèi)存申請一塊連續(xù)可用的空間,有幾點需要注意
1.如果開辟成功的話,返回指向開辟好空間的指針
2.如果開辟失敗的話,則返回NULL,因此每次開辟空間之后,都要進(jìn)行檢查
3.malloc函數(shù)未定義返回類型,一切由使用者自己使用
4.需引用stdlib.h頭文件
free函數(shù)是和malloc配套使用的,每次在堆開辟動態(tài)空間后,程序結(jié)束之前,必須進(jìn)行空間釋放,不然會出現(xiàn)動態(tài)空間泄露,在使用free時,仍需要注意幾點
1.如果指針指向的空間不是動態(tài)開辟的,不能用free進(jìn)行釋放
2.如果指針指向的是null指針,則free函數(shù)什么事都不做
3.free不能多次使用
4.需引用stdlib.h頭文件
代碼如下(示例):
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { int* src = NULL; src = (int*)malloc(40);//開辟40字節(jié)動態(tài)內(nèi)存 if (src == NULL) { printf("%s", strerror(errno)); return 1; } free(src);//進(jìn)行動態(tài)內(nèi)存釋放 src = NULL; return 0; }
相信有人會問,不是已經(jīng)對動態(tài)內(nèi)存進(jìn)行釋放,為什么還要令指針等于NULL,我們調(diào)試一把。
這里我們可以發(fā)現(xiàn),雖然動態(tài)內(nèi)存進(jìn)行free釋放,但指針仍然指向被釋放的動態(tài)內(nèi)存的地址,如果不置空,就會造成野指針,非法訪問的問題。
2.calloc
calloc和malloc最大的區(qū)別就是,malloc只負(fù)責(zé)對內(nèi)存進(jìn)行動態(tài)開辟,但calloc不僅開辟,還進(jìn)行初始化。
代碼如下(示例):
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { int* src = (int*)calloc(10, sizeof(int)); if (src == NULL) { printf("%s", strerror(errno)); return 1; } free(src);//進(jìn)行動態(tài)內(nèi)存釋放 src = NULL; return 0; }
我們調(diào)試一把可以發(fā)現(xiàn),calloc在開辟空間時同時進(jìn)行了初始化。所以如何我們對申請的內(nèi)存空間的內(nèi)容要求初始化,那么可以很方便的使用calloc函數(shù)來完成任務(wù)。
3.realloc
當(dāng)我們一次開辟動態(tài)內(nèi)存不夠大的時候,realloc讓動態(tài)內(nèi)存更加的靈活。realloc幾個參數(shù):
1.第一個參數(shù)為要調(diào)整內(nèi)存的地址
2.調(diào)整后大小
3.調(diào)整后內(nèi)存的起始位置
為什么還要返回調(diào)整后內(nèi)存的地址,不是直接就開辟好了嗎?其實reallloc函數(shù)在開辟時有以下兩種情況:
1.原來的內(nèi)存之后空間是足夠的,則直接開辟
2.原來的內(nèi)存之后空間不夠用。
我們畫圖刨析一下
情況1:直接追加空間,原來數(shù)據(jù)不變
情況2:沒有足夠的空間,在堆上找一個大小合適的連續(xù)空間。所以函數(shù)返回的是一個新的內(nèi)存地址。
代碼如下(示例)
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { int* src = NULL; src = (int*)malloc(40);//開辟40字節(jié)動態(tài)內(nèi)存 if (src == NULL) { printf("%s", strerror(errno)); return 1; } src = realloc(src, 80); if (src == NULL) { printf("%s", strerror(errno)); return 1; } free(src);//進(jìn)行動態(tài)內(nèi)存釋放 src = NULL; return 0; }
三、動態(tài)內(nèi)存函數(shù)常見錯誤
1.動態(tài)內(nèi)存越界訪問
void test1() { int* src = (int*)malloc(20); if (NULL == src) { return 1; } int i = 0; for (i = 0; i < 6; i++) { (*src+i)=i } free(src); src = NULL; }
在這里我們malloc只開辟了20個字節(jié),但(*src+5)造成了越界訪問
2.對NULL指針進(jìn)行解引用操作
void test2() { int* src = (int*)malloc(INT_MAX);//此處INT_MAX為int的最大值 *src = 10;//如果src是NULL時,無法解引用 free(src); return 0; }
這里未對開辟的動態(tài)內(nèi)存空間進(jìn)行是否為空的判斷,當(dāng)為空時,解引用就會出現(xiàn)錯誤。
3.使用free釋放一塊動態(tài)開辟內(nèi)存的一部分
void test3() { int* src = (int*)malloc(40); int i=0; for(i=0;i<6;i++) { *(src+i)=i; src++; } free(src);//此時src不指向起始位置 }
因為指針指向的地址發(fā)生變化,不在指向起始未知,進(jìn)行free釋放是非常危險的。
4.對靜態(tài)內(nèi)存進(jìn)行free釋放
void test4() { int a = 20; int* src = &a; free(src); }
5.對同一內(nèi)存空間多次釋放
void test5() { int* src = (int*)malloc(40); free(src); free(src);//多次釋放 }
第一個free已經(jīng)將堆空間的動態(tài)內(nèi)存進(jìn)行釋放,此時src已經(jīng)是一個野指針,在進(jìn)行釋放是十分危險的。
6.動態(tài)開辟空間忘記釋放
void test6() { int* src = (int*)malloc(40); if (src != NULL) { } while (1); }
在開辟動態(tài)內(nèi)存之后,一直進(jìn)行while循環(huán),為進(jìn)行free釋放,會造成內(nèi)存泄漏。
四、經(jīng)典筆試題
1.筆試1
void test(char* src) { src = (char*)malloc(30); } int main() { char* src = NULL; test(src); strcpy(src, "wo yao jin da chang"); printf(src); free(src); }
這里會輸出wo yao jin da chang 嗎?
這里很明顯,src仍然是NULL,所以無法輸出wo yao jin da chang
2.筆試2
char* test() { char arr[] = "wo yao jin da chang"; return arr; } int main() { char* src = NULL; src = tset(); printf(src); return 0; }
這里會輸出wo yao jin da chang 嗎?
這里test函數(shù)確實把字符串地址傳給了src,但是字符串是局部變量,當(dāng)函數(shù)執(zhí)行完之后,就銷毀了,所以src輸出的內(nèi)容是隨機的。
3.筆試3
void test() { char* src = (char*)malloc(50); if (src != NULL) { strcpy(src, "wo yao jin da chang"); } free(src); if (src != NULL) { strcpy(src, "taijuanlebujinle"); printf(src); } }
這里會輸出taijuanlebujinle 嗎?
這里對動態(tài)內(nèi)存釋放后,繼續(xù)進(jìn)行賦值,對野指針進(jìn)行了訪問是錯誤的。
總結(jié)
看到這里大家對動態(tài)內(nèi)存管理已經(jīng)有了一定的認(rèn)識,應(yīng)該特別注意這幾點,在進(jìn)行動態(tài)內(nèi)存開辟之后進(jìn)行判斷是否為空,使用完后進(jìn)行free釋放,并且置空,防止動態(tài)內(nèi)存泄露,只要記住這幾點基本就可以很好的使用動態(tài)內(nèi)存了。
以上就是深入了解C語言的動態(tài)內(nèi)存管理的詳細(xì)內(nèi)容,更多關(guān)于C語言動態(tài)內(nèi)存管理的資料請關(guān)注腳本之家其它相關(guān)文章!
- 詳解C語言中動態(tài)內(nèi)存管理及柔性數(shù)組的使用
- C語言動態(tài)內(nèi)存的分配最全面分析
- 一文帶你搞懂C語言動態(tài)內(nèi)存管理
- 詳解C語言中的動態(tài)內(nèi)存管理
- C語言動態(tài)內(nèi)存分配圖文講解
- 使用c語言輕松實現(xiàn)動態(tài)內(nèi)存管
- 一文帶你了解C語言中的動態(tài)內(nèi)存管理函數(shù)
- C語言動態(tài)內(nèi)存管理的原理及實現(xiàn)方法
- 詳解C語言中動態(tài)內(nèi)存管理
- C語言中常見的六種動態(tài)內(nèi)存錯誤總結(jié)
- 一文解析C語言中動態(tài)內(nèi)存管理
- C語言動態(tài)內(nèi)存管理的實現(xiàn)示例
相關(guān)文章
C++實現(xiàn)LeetCode(95.獨一無二的二叉搜索樹之二)
這篇文章主要介紹了C++實現(xiàn)LeetCode(95.獨一無二的二叉搜索樹之二),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語言詳細(xì)分析結(jié)構(gòu)體的內(nèi)存對齊規(guī)則
C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲不同類型的數(shù)據(jù)項,本篇讓我們來了解C 的結(jié)構(gòu)體內(nèi)存對齊2022-07-07C++ 學(xué)習(xí)之旅 Windows程序內(nèi)部運行原理
學(xué)習(xí)C++與.net不同的是,一定要搞清楚Windows程序內(nèi)部運行原理,因為他所涉及大多數(shù)是操作系統(tǒng)的調(diào)用,而.net畢竟是在.netFrameWork上唱戲2012-11-11如何用c++表驅(qū)動替換if/else和switch/case語句
本文將介紹使用表驅(qū)動法,替換復(fù)雜的if/else和switch/case語句,想了解詳細(xì)內(nèi)容,請看下文2021-08-08