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