- 普通變量建立開辟空間大小是固定的
- 數組在申明的時候,必須指定數組的長度,它所需要的記憶體在編譯時配置設定(
)
柔性數組除外,後文會寫到
在記憶體中如圖
是以就有了動态記憶體
動态記憶體函數
1)malloc
void* malloc( size_t size ); size是配置設定未初始化儲存的位元組大小
。
開辟成功,則傳回這個開辟的空間的指針
。
開辟失敗,則傳回NULL
- 傳回值的類型是 void* ,是以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己來決定。
- 如果參數 size 為0,malloc的行為是标準是未定義的,取決于編譯器
int* ptr = NULL;
ptr = (int*)malloc(num*sizeof(int));//注意malloc的強轉類型
if(NULL != ptr)//判斷ptr指針是否為空
{
int i = 0;
for(i=0; i<num; i++)
{
*(ptr+i) = 0;
}
}
free(ptr);//釋放ptr所指向的動态記憶體
ptr = NULL;//避免野指針
2)calloc
void* calloc( size_t num, size_t size ); num是數組元素個數 size是元素類型大小
- 函數的功能是為 num 個大小為 size 的元素開辟一塊空間,并且把空間的每個位元組初始化為0。
與函數 malloc 的差別隻在于 calloc 會在傳回位址之前把申請的空間的每個位元組全初始化為0
3)realloc
void *realloc( void *ptr, size_t new_size );size為新空間的大小(
ptr為前面malloc、calloc開辟空間的位址
)
注意,是總大小不是新開辟的空間大小
- 傳回值為調整之後的記憶體起始位置。
- 這個函數調整原記憶體空間大小的基礎上,還會将原來記憶體中的資料移動到 新 的空間
realloc在調整記憶體空間的場景:
- 原有空間之後沒有足夠大的空間,
函數會找一塊夠用的空間,同時把原來的資料copy下來,傳回新開辟空間的位址,同時free掉原空間
- 原來的空間後面有足夠的空間 緊接着直接開辟新空間
如果無空間可增加 則傳回空指針,是以下面的代碼是錯誤的
- 當沒有空間時原來malloc開辟的空間的資料也會丢失
是以需要中間變量來确定realloc是否為空指針後再賦予p
//錯誤代碼
p=realloc(...) //p為malloc開開辟的空間位址
4)free
void free( void* ptr );
如果參數 ptr 指向的空間不是動态開辟的,那free函數的行為是未定義的。
如果參數 ptr 是NULL指針,則函數什麼事都不做
- free隻能free堆區
動态記憶體常見錯誤
1)對NULL指針的解引用操作
注意: 局部變量未初始化為随機值,不能解引用
//錯誤代碼
void inside()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//當p為空指針,出現問題
)
2)對動态開辟空間的越界通路
邏輯錯誤
//錯誤代碼
int *p = (int *)malloc(10*sizeof(int));
for(int i=0; i<=10; i++)
{
*(p+i) = i;//當i是10的時候越界通路
}
free(p);
3)對非動态開辟記憶體使用free釋放
int a = 10;
棧區開辟空間
//錯誤代碼
int a = 10;
int *p = &a;
free(p);//程式會崩潰
4)使用free釋放一塊動态開辟記憶體的一部分
p不再指向動态記憶體的起始位置
//錯誤代碼
int *p = (int *)malloc(100);
p++;
free(p);//p++之前的空間将無法被釋放
5)對同一塊動态記憶體多次釋放
詳細剖析參考:為什麼重複free()比記憶體洩漏危害更大
解決辦法:free()後将p置為空指針 p=NULL;
//錯誤代碼
int *p = (int *)malloc(100);
free(p);
free(p);//重複釋放,程式崩潰
6)動态開辟記憶體忘記釋放(記憶體洩漏)
忘記釋放不再使用的動态開辟的空間會造成記憶體洩漏
//錯誤代碼
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;//沒有free!!
}
>>四道題充分體會動态記憶體錯誤<<
C99柔性數組
在不同編譯器中的兩種寫法
typedef struct flexible_array
{
int i;
int a[0];//柔性數組成員
}flexible_array;
typedef struct flexible_array
{
int i;
int a[];//柔性數組成員
}flexible_array;
為柔性數組開辟空間與使用
int i=0;
flexible_array *p = (flexible_array*)malloc(sizeof(flexible_array)+100*sizeof(int));
//如要繼續開辟空間使用realloc即可
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
與結構體中的普通數組開辟空間比較
//普通數組
typedef struct normal_a
{
int i;
int *p_a;
}normal_a;
normal_a *p = malloc(sizeof(normal_a));
p->i = 100;
p->p_a = (int *)malloc(p->i*sizeof(int));
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;
柔性數組優勢
- 友善記憶體釋放
- 有利于通路速度(
)
連續的記憶體有益于提高通路速度,也有益于減少記憶體碎片
更深入了解 請參見陳皓大佬的>>C語言結構體裡的成員數組和指針<<