C語言中內存管理主要分為以下幾塊:
- 棧區
- 堆區
- 全局區或靜態區
- 字符常量區
- 程序代碼區
一般棧區都是由系統自動分配回收,但是棧區大小是有限制的,windows下一般為程序分配的棧內存為2M左右。超出就會拋出stack overflow錯誤.
如下:
void main(){
//40M
//靜態內存分配
int a[1024 * 1024 * 10];
getchar();
}
上面的代碼運行時就會拋出 stack overflow。這種情況下,就需要程序人員,手動在堆區分配與釋放內存。
在C語言中內存管理主要通過以下幾個函數進行:
- malloc()
- realloc()
- calloc()
- free()
malloc與calloc函數都是進行堆內存分配,區別主要在于傳參形式不一樣:
//8M
int * p = malloc(1024 * 1024 * 2 * sizeof(int));
//8M
int * p2 = calloc(1024 * 1024 * 2,sizeof(int));
realloc函數用于重新分配內存。分為兩種情況:
- 縮?。嚎s小的那一部分數據會丟失。
- 增大:
- 如果當前內存段后面有需要的內存空間,直接擴展這段內存空間,realloc返回原指針
- 如果當前內存段后面的空閑字節不夠,那么就使用堆中的第一個能夠滿足這一要求的內存塊,將目前的數據復制到新的位置,并將原來的數據庫釋放掉,返回新的內存地址
- 如果申請失敗,返回NULL,原來的指針仍然有效
示例:
void main(){
int len;
printf("第一次輸入數組的長度:");
scanf("%d", &len);
//int* p = malloc(len * sizeof(int));
int* p = calloc(len, sizeof(int));
int i = 0;
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
int addLen;
printf("輸入數組增加的長度:");
scanf("%d", &addLen);
//內存不夠用,擴大剛剛分配的內存空間
//1.原來內存的指針 2.內存擴大之后的總大小
int* p2 = realloc(p, sizeof(int)* (len + addLen));
if (p2 == NULL){
printf("重新分配失敗");
free(p);
p = NULL;
return;
}
//重新賦值
i = 0;
printf("--------------------------\n");
for (; i < len + addLen; i++){
p2[i] = rand() % 200;
printf("%d,%#x\n", p2[i], &p2[i]);
}
//手動釋放內存
if (p2 != NULL){
free(p2);
p2 = NULL;
}
getchar();
}
free函數用于釋放內存。如上所示。
Note:同一塊內存區不能多次釋放,釋放完之后(指針仍然有值),一般地給指針置NULL,標志釋放完成,避免這種情況。重新分配內存成功后,如果是原地址,上面即是p =p2.如果是新開辟的內存,則p會被系統自動釋放,所以后面只釋放p2即可。
下面再看一個內存分配的例子:
void main(){
int len = 4;
char ** args = malloc(sizeof(char*)*len);
int i = 0;
for ( ; i < 4; i++)
{
args[i] = malloc(sizeof(char)* 20);
sprintf(args[i],"array[%d]",i);
}
for ( i = 0; i < 4; i++)
{
free(args[i]);
}
free(args);
getchar();
}
上面的示例中,我們進行了兩次開辟,好像跟上面講的不一樣,數組開辟空間后,為啥單個元素還要再次開辟,而上面的數組卻沒有?其實不然。
這里我們定義的是一個字符串數組。但是C中是沒有字符串的,每一個字符串其實是一個字符數組,所以,它相當于是一個二維數組,所以需要再次分配內存。
我們再舉個二維數組,動態分配的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n1,n2;
int **array,i,j;
printf("請輸入所要創建的動態數組的第一維長度:");
scanf("%d",&n1);
printf("請輸入所要創建的動態數組的第二維長度:");
scanf("%d",&n2);
array=(int**)malloc(n1*sizeof(int*)); //第一維
for(i=0;i<n1; i++)
{
array[i]=(int*)malloc(n2* sizeof(int));//第二維
}
for(i=0;i<n1;i++)
{
for(j=0;j<n2;j++)
{
array[i][j]=i*n2+j+1;
printf("%d\t",array[i][j]);
}
printf("\n");
}
for(i=0;i<n1;i++)
{
free(array[i]);//釋放第二維指針
}
free(array);//釋放第一維指針
return 0;
}
這兩個例子,對比著看,相信就不會有啥疑惑了。