如何理解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)服務(wù)商,為中小企業(yè)提供成都網(wǎng)站建設(shè)、網(wǎng)站制作服務(wù),網(wǎng)站設(shè)計(jì),綿陽(yáng)服務(wù)器托管等一站式綜合服務(wù)型公司,專(zhuān)業(yè)打造企業(yè)形象網(wǎng)站,讓您在眾多競(jìng)爭(zhēng)對(duì)手中脫穎而出創(chuàng)新互聯(lián)公司。
在一般的程序中,我們難免會(huì)遇到動(dòng)態(tài)的申請(qǐng)內(nèi)存,那么動(dòng)態(tài)內(nèi)存分配的意義到底是什么呢?在 C 語(yǔ)言中的一切操作都是基于內(nèi)存的,變量和數(shù)組都是內(nèi)存的別名。內(nèi)存分配由編譯器在編譯期間決定,定義數(shù)組的時(shí)候必須指定數(shù)組長(zhǎng)度,數(shù)組長(zhǎng)度當(dāng)然也是在編譯期就必須確定的。
那么為什么會(huì)有動(dòng)態(tài)分配內(nèi)存的需求呢?在程序運(yùn)行的過(guò)程中,可能需要使用一些額外的內(nèi)存空間。我們都是在 C 語(yǔ)言中使用 malloc 來(lái)動(dòng)態(tài)申請(qǐng)內(nèi)存的,當(dāng)時(shí)釋放的時(shí)候是用 free,下來(lái)我們看看 malloc 和 free 用于執(zhí)行動(dòng)態(tài)內(nèi)存分配和釋放時(shí)是怎樣進(jìn)行的,用下圖進(jìn)行說(shuō)明
我們可以看到程序通過(guò) malloc 在內(nèi)存池中進(jìn)行申請(qǐng),那么歸還則是通過(guò) free 進(jìn)行釋放的。如果我們只是 malloc 進(jìn)行申請(qǐng)而不 free,那么我們的內(nèi)存池將會(huì)被用完,那么程序也就崩潰了。這就是我們平時(shí)所說(shuō)的內(nèi)存泄漏。
malloc 所分配的是一塊連續(xù)的內(nèi)存,它是以字節(jié)為單位并且不帶任何的類(lèi)型信息。free 則是用于將動(dòng)態(tài)內(nèi)存歸還系統(tǒng)。這兩個(gè)函數(shù)的原型是 void* malloc(size_t size); void free(void* pointer);我們得注意這么幾點(diǎn):a> malloc 和 free 是庫(kù)函數(shù),而不是系統(tǒng)調(diào)用;b> malloc 實(shí)際分配的內(nèi)存可能會(huì)比請(qǐng)求的多;c> 不能依賴于不同平臺(tái)下的 malloc 行為;d> 當(dāng)請(qǐng)求的動(dòng)態(tài)內(nèi)存無(wú)法滿足時(shí),malloc 返回 NULL;e> 當(dāng) free 的參數(shù)為 NULL時(shí),函數(shù)直接返回。
那么我們接下來(lái)思考下,malloc(0) 將返回什么?是會(huì)報(bào)錯(cuò)?還是啥也不做?還是會(huì)出現(xiàn)不確定的結(jié)果?我們做下實(shí)驗(yàn)看看
#include <stdio.h> int main() { int* p = (int*)malloc(0); printf("p = %p\n", p); return 0; }
我們看看編譯結(jié)果
我們看到編譯器給出警告了,但是還是成功執(zhí)行了。其實(shí)我們平時(shí)所說(shuō)的內(nèi)存有兩個(gè)概念,一個(gè)是它的起始地址,一個(gè)是大小。在這塊我們就好解釋了,malloc(0) 只是申請(qǐng)的內(nèi)存大小為0而已,但是它還會(huì)有起始地址。所以如果當(dāng)我們?cè)诔绦蛑袩o(wú)限次的 malloc(0) 時(shí),程序最終會(huì)崩潰,因?yàn)樗牡刂沸畔⒁矔?huì)占用空間。
下來(lái)我們?cè)倏匆粋€(gè)代碼,是唐長(zhǎng)老從實(shí)際工程中抽象出來(lái)的內(nèi)存檢測(cè)模塊
test.c 源碼
#include <stdio.h> #include "mleak.h" void f() { MALLOC(100); } int main() { int* p = (int*)MALLOC(3 * sizeof(int)); f(); p[0] = 1; p[1] = 2; p[2] = 3; FREE(p); PRINT_LEAK_INFO(); return 0; }
mleak.h 源碼
#ifndef _MLEAK_H_ #define _MLEAK_H_ #include <malloc.h> #define MALLOC(n) mallocEx(n, __FILE__, __LINE__) #define FREE(p) freeEx(p) void* mallocEx(size_t n, const char* file, const line); void freeEx(void* p); void PRINT_LEAK_INFO(); #endif
mleak.c 源碼
#include "mleak.h" #define SIZE 256 /* 動(dòng)態(tài)內(nèi)存申請(qǐng)參數(shù)結(jié)構(gòu)體 */ typedef struct { void* pointer; int size; const char* file; int line; } MItem; static MItem g_record[SIZE]; /* 記錄動(dòng)態(tài)內(nèi)存申請(qǐng)的操作 */ void* mallocEx(size_t n, const char* file, const line) { void* ret = malloc(n); /* 動(dòng)態(tài)內(nèi)存申請(qǐng) */ if( ret != NULL ) { int i = 0; /* 遍歷全局?jǐn)?shù)組,記錄此次操作 */ for(i=0; i<SIZE; i++) { /* 查找位置 */ if( g_record[i].pointer == NULL ) { g_record[i].pointer = ret; g_record[i].size = n; g_record[i].file = file; g_record[i].line = line; break; } } } return ret; } void freeEx(void* p) { if( p != NULL ) { int i = 0; /* 遍歷全局?jǐn)?shù)組,釋放內(nèi)存空間,并清除操作記錄 */ for(i=0; i<SIZE; i++) { if( g_record[i].pointer == p ) { g_record[i].pointer = NULL; g_record[i].size = 0; g_record[i].file = NULL; g_record[i].line = 0; free(p); break; } } } } void PRINT_LEAK_INFO() { int i = 0; printf("Potential Memory Leak Info:\n"); /* 遍歷全局?jǐn)?shù)組,打印未釋放的空間記錄 */ for(i=0; i<SIZE; i++) { if( g_record[i].pointer != NULL ) { printf("Address: %p, size:%d, Location: %s:%d\n", g_record[i].pointer, g_record[i].size, g_record[i].file, g_record[i].line); } } }
我們看到在 test.c 中第6行 f() 函數(shù)中動(dòng)態(tài)申請(qǐng)了內(nèi)存,但是沒(méi)有進(jìn)行釋放。由于是局部的,當(dāng)這個(gè)函數(shù)調(diào)用完后,將產(chǎn)生內(nèi)存泄漏。那么我們?cè)诘?21 行將會(huì)打印出信息。我們這個(gè)對(duì)應(yīng)的函數(shù)是怎么實(shí)現(xiàn)的呢,在 mleak.c 中將申請(qǐng)得到的內(nèi)存地址放入一個(gè)數(shù)組中,在后面會(huì)進(jìn)行檢查,如果進(jìn)行 FREE 操作,便會(huì)在數(shù)組中對(duì)應(yīng)的刪除標(biāo)記,否則標(biāo)記存在。如果標(biāo)記存在,我們則會(huì)打印出對(duì)應(yīng)的信息來(lái)。我們來(lái)看看編譯結(jié)果
我們看到在地址為 0x9d13018 處存在100大小的內(nèi)存沒(méi)進(jìn)行釋放,它位于 test.c 的第6行。下來(lái)我們注釋掉 teat.c 中的第19行,看看這個(gè)內(nèi)存沒(méi)進(jìn)行釋放是否會(huì)打印出來(lái)
我們看到一樣的打印出來(lái)了。證明我們這個(gè)內(nèi)存泄漏的檢測(cè)模塊還是很準(zhǔn)的。
下來(lái)我們?cè)趤?lái)看看 calloc 和 realloc,它們是 malloc 的同胞兄弟,原型分別為:void* calloc(size_t num, size_t size); void* realloc(void* pointer, size_t new_size);那么 calloc 的參數(shù)代表所返回內(nèi)存的類(lèi)型信息,其中 calloc 會(huì)將返回的內(nèi)存初始化為 0;realloc 用于修改一個(gè)原先已經(jīng)分配 的內(nèi)存塊大小,在使用 realloc 之后應(yīng)該使用其返回值,當(dāng) pointer 的第一個(gè)參數(shù)為 NULL 時(shí),等價(jià)于 malloc。
下來(lái)我們以代碼為例進(jìn)行分析
#include <stdio.h> #include <malloc.h> #define SIZE 5 int main() { int i = 0; int* pI = (int*)malloc(SIZE * sizeof(int)); short* pS = (short*)calloc(SIZE, sizeof(short)); for(i=0; i<SIZE; i++) { printf("pI[%d] = %d, pS[%d] = %d\n", i, pI[i], i, pS[i]); } printf("Before: pI = %p\n", pI); pI = (int*)realloc(pI, 2 * SIZE * sizeof(int)); printf("After: pI = %p\n", pI); for(i=0; i<10; i++) { printf("pI[%d] = %d\n", i, pI[i]); } free(pI); free(pS); return 0; }
我們看看編譯結(jié)果
按照我們前面講的,數(shù)組 pI 中的數(shù)應(yīng)該是隨機(jī)數(shù)的,數(shù)組 pS 的數(shù)是被初始化為 0 的??墒乾F(xiàn)在全是0,別著急,這只是 gcc 中做的優(yōu)化。我們看到數(shù)組 pI 在調(diào)用 realloc 后的大小確實(shí)改變了,并且地址也變了。下來(lái)我們看看 BCC 編譯器中是怎樣的
我們看到數(shù)組 pI 中的數(shù)確實(shí)是隨機(jī)數(shù)了,而數(shù)組 pS 中的數(shù)依舊全是 0。
總結(jié)如下:
1、動(dòng)態(tài)內(nèi)存分配是 C 語(yǔ)言中的強(qiáng)大功能,程序能夠在需要的時(shí)候有機(jī)會(huì)使用更多的內(nèi)存;
2、malloc 單純的從系統(tǒng)中申請(qǐng)固定字節(jié)大小的內(nèi)存,calloc 能以類(lèi)型大小為單位申請(qǐng)內(nèi)存并初始化為0,realloc 用于重置內(nèi)存大小。
看完上述內(nèi)容,你們掌握如何理解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
當(dāng)前標(biāo)題:如何理解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存分配
URL地址:http://m.rwnh.cn/article6/ipjcig.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、品牌網(wǎng)站設(shè)計(jì)、小程序開(kāi)發(fā)、搜索引擎優(yōu)化、云服務(wù)器、手機(jī)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)