C語(yǔ)言程序設(shè)計(jì)中,動(dòng)態(tài)內(nèi)存分配如何實(shí)現(xiàn),需要注意哪些問題?
1、動(dòng)態(tài)內(nèi)存分配用malloc函數(shù),他的函數(shù)原型
void * malloc (size_t size);
malloc有一個(gè)參數(shù)size,表示需要申請(qǐng)的內(nèi)存空間大小,單位是字節(jié)。
- 分配的內(nèi)存空間連續(xù),如果沒有空閑內(nèi)存,可能分配失敗
- 返回值為void*類型,也就是沒有確定具體的數(shù)據(jù)類型,由用戶自己決定,也就是需要強(qiáng)制數(shù)據(jù)類型轉(zhuǎn)換
// 動(dòng)態(tài)內(nèi)存分配
#include < stdio.h >
#include < stdlib.h >
#define SIZE 5
void display(int *p, int n){
int i;
for(i = 0; i < n; i ++){
printf("%5dn", p[i]);
}
}
int main(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < SIZE; i ++){
p[i] = i;
}
display(p, SIZE);
free(p);
return 0;
}
案例中,分配了一個(gè)大小為SIZEsizeof(int)個(gè)字節(jié)的內(nèi)存空間,強(qiáng)制轉(zhuǎn)換為int類型,并由指針p指向該內(nèi)存空間。
sizeof(int *); // 求出int *類型變量占據(jù)的內(nèi)存大小
sizeof(int);// 求出int類型變量占據(jù)的內(nèi)存大小
int *p; sizeof(*p); // 求出指針p所存放的地址占據(jù)的內(nèi)存大小
假設(shè)int類型變量占據(jù)4個(gè)字節(jié)的內(nèi)存,那么總共分配了20個(gè)字節(jié)的內(nèi)存空間。
2、malloc分配的內(nèi)存屬于堆內(nèi)存
所謂堆內(nèi)存,他的生命周期與進(jìn)程相同。
int* initArr(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < SIZE; i ++){
p[i] = i;
}
return p;
}
int main(){
int *p = initArr();
display(p, SIZE);
free(p);
return 0;
}
案例中,雖然動(dòng)態(tài)內(nèi)存是在函數(shù)initArr中申請(qǐng)到的,它的作用域應(yīng)該是函數(shù)內(nèi)部,但是在主函數(shù)main中依然可以使用這個(gè)內(nèi)存,正是由于該內(nèi)存屬于堆內(nèi)存,生命周期與進(jìn)程相同,不會(huì)因?yàn)楹瘮?shù)調(diào)用結(jié)束而釋放。
同樣的,正是因?yàn)樵搩?nèi)存沒有釋放,才可以在函數(shù)display中繼續(xù)使用。
3、內(nèi)存釋放函數(shù)free,函數(shù)原型
void free(void *ptr)
由于malloc申請(qǐng)的內(nèi)存屬于堆內(nèi)存,生命周期較長(zhǎng),所以在使用完之后,如果后面的程序再也用不到該內(nèi)存,就應(yīng)該提前將其釋放,釋放malloc申請(qǐng)的內(nèi)存用free函數(shù)。
free函數(shù)有一個(gè)參數(shù),指向?qū)⒁尫诺膬?nèi)存塊,所以是一個(gè)指針,沒有返回值。
上面的案例中,在主函數(shù)返回之前,內(nèi)存使用完之后,就直接釋放了該內(nèi)存。需要注意的是,如果后面還需要繼續(xù)使用該內(nèi)存,切不可提前釋放。
int main(){
int *p = initArr();
free(p);
display(p, SIZE);
return 0;
}
這必然是一個(gè)錯(cuò)誤的示例,如果提前釋放了該內(nèi)存,后面就找不到相應(yīng)的內(nèi)存,也就不能繼續(xù)對(duì)該空間進(jìn)行操作。
free(p);
p=NULL;
在釋放動(dòng)態(tài)內(nèi)存之后,最好將原來的指針設(shè)置為空,防止指針p成為野指針被使用。
4、malloc數(shù)組的溢出
#define SIZE 5
#define N 7
int* initArr(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < N; i ++){
p[i] = i;
}
return p;
}
int main(){
int *p = initArr();
display(p, N);
free(p);
return 0;
}
案例打印的結(jié)果如下
0
1
2
3
4
5
6
是的,你沒有看錯(cuò),本來申請(qǐng)的是5個(gè)元素空間,這里打印了7個(gè)元素,而且沒有報(bào)錯(cuò)。這就是mallo函數(shù)申請(qǐng)的內(nèi)存與靜態(tài)數(shù)組的區(qū)別,malloc申請(qǐng)的內(nèi)存屬于堆內(nèi)存,雖然指定的內(nèi)存只有5個(gè)元素大小,但是后面依然可以訪問。
這是一個(gè)非常危險(xiǎn)的操作,因?yàn)槟悴恢涝浇缰?,?duì)越界后的內(nèi)存修改,會(huì)不會(huì)影響其他程序。
int a[5];
for(int i=0; i < 6; i ++){
a[i]=i;
}
在這個(gè)靜態(tài)數(shù)組中,必然會(huì)報(bào)錯(cuò),程序執(zhí)行到后面的時(shí)候,可能執(zhí)行失敗,因?yàn)橐呀?jīng)越界。靜態(tài)數(shù)組存儲(chǔ)在棧內(nèi)存中,屬于動(dòng)態(tài)存儲(chǔ)區(qū),他不允許越界操作。
5、malloc函數(shù)可開辟的最大空間
malloc開辟的空間屬于堆內(nèi)存,靜態(tài)數(shù)組屬于棧內(nèi)存,兩者的最大容量存在差異。
#define SIZE 102400000000000
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
在你的計(jì)算機(jī)上也許可以成功申請(qǐng)到這么大的內(nèi)存,但是如果用靜態(tài)數(shù)組,這個(gè)操作很可能失敗。
#define SIZE 102400000000000
int a[SIZE];
a[0]=0;
printf("%dn", a[0]);
一般情況下,靜態(tài)數(shù)組允許申請(qǐng)的最大連續(xù)空間,小于動(dòng)態(tài)數(shù)組允許申請(qǐng)的最大連續(xù)空間。
6、calloc函數(shù)
void *calloc(size_t nitems, size_t size)
calloc函數(shù)與malloc函數(shù)功能相同,不同點(diǎn)是:calloc函數(shù)會(huì)對(duì)所有元素進(jìn)行初始化,初始化為0。
calloc函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是將要申請(qǐng)的元素個(gè)數(shù),第二個(gè)參數(shù)是每個(gè)元素的內(nèi)存大小。
int* initArr2(){
int *p = (int *)calloc(SIZE, sizeof(int));
if(!p) exit(-1);
return p;
}
int main(){
int *p = initArr2();
display(p, N);
free(p);
return 0;
}
案例中,申請(qǐng)了一個(gè)動(dòng)態(tài)內(nèi)存,并對(duì)該內(nèi)存進(jìn)行初始化,所有元素都是0,因此,在不給每個(gè)元素賦值的情況下,打印出來的全部是0。
calloc函數(shù)分配的內(nèi)存也是堆內(nèi)存,他與malloc相同,存在的問題也相同。
7、realloc函數(shù)
嘗試重新調(diào)整之前調(diào)用 malloc 或 calloc 所分配的 ptr 所指向的內(nèi)存塊的大小。
void *realloc(void *ptr, size_t size)
第一個(gè)參數(shù)表示指向已經(jīng)申請(qǐng)到的動(dòng)態(tài)內(nèi)存塊,如果為空指針,則會(huì)重新分配一個(gè)新內(nèi)存塊。第二個(gè)參數(shù)表示新內(nèi)存塊的大小,可以比原來的內(nèi)存塊大,也可以比原來內(nèi)存塊小。
int* initArr3(int *p){
int *pnew = (int *)realloc(p, (SIZE + SIZE) * sizeof(int));
if(!pnew) exit(-1);
for(int i = 0; i < N; i ++){
pnew[i] = i;
}
return pnew;
}
int main(){
int *p = initArr3(NULL);
display(p, N);
free(p);
return 0;
}
傳入的參數(shù)為NULL,可以重新分配一個(gè)內(nèi)存塊。
int main(){
int *p = initArr2();
display(p, N);
int *pnew = initArr3(p);
display(pnew, N);
// display(p, N);//此時(shí)原來的數(shù)組已經(jīng)不存在
free(pnew);
return 0;
}
傳入一個(gè)已經(jīng)指向的內(nèi)存,在原來的基礎(chǔ)上進(jìn)行擴(kuò)大或者縮小,返回新內(nèi)存的首地址。原來的內(nèi)存將會(huì)被釋放。
realloc函數(shù)與malloc、calloc函數(shù)類似,也會(huì)存在malloc函數(shù)類似的問題。
以上是C語(yǔ)言動(dòng)態(tài)申請(qǐng)內(nèi)存的相關(guān)內(nèi)容,動(dòng)手嘗試驗(yàn)證一下上述問題。
評(píng)論