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