天天看點

C語言----動态記憶體

  1. 普通變量建立開辟空間大小是固定的
  2. 數組在申明的時候,必須指定數組的長度,它所需要的記憶體在編譯時配置設定(

    柔性數組除外,後文會寫到

在記憶體中如圖

是以就有了動态記憶體

C語言----動态記憶體

動态記憶體函數

1)malloc

void* malloc( size_t size );

size是配置設定未初始化儲存的位元組大小

  1. 開辟成功,則傳回這個開辟的空間的指針

  2. 開辟失敗,則傳回NULL

  3. 傳回值的類型是 void* ,是以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己來決定。
  4. 如果參數 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是元素類型大小

  1. 函數的功能是為 num 個大小為 size 的元素開辟一塊空間,并且把空間的每個位元組初始化為0。
  2. 與函數 malloc 的差別隻在于 calloc 會在傳回位址之前把申請的空間的每個位元組全初始化為0

3)realloc

void *realloc( void *ptr, size_t new_size );

ptr為前面malloc、calloc開辟空間的位址

size為新空間的大小(

注意,是總大小不是新開辟的空間大小

  1. 傳回值為調整之後的記憶體起始位置。
  2. 這個函數調整原記憶體空間大小的基礎上,還會将原來記憶體中的資料移動到 新 的空間
realloc在調整記憶體空間的場景:
  1. 原有空間之後沒有足夠大的空間,

    函數會找一塊夠用的空間,同時把原來的資料copy下來,傳回新開辟空間的位址,同時free掉原空間

  2. 原來的空間後面有足夠的空間 緊接着直接開辟新空間
  3. 如果無空間可增加 則傳回空指針,是以下面的代碼是錯誤的

  4. 當沒有空間時原來malloc開辟的空間的資料也會丢失
  5. 是以需要中間變量來确定realloc是否為空指針後再賦予p

//錯誤代碼
p=realloc(...) //p為malloc開開辟的空間位址
           

4)free

void free( void* ptr );
  1. 如果參數 ptr 指向的空間不是動态開辟的,那free函數的行為是未定義的。

  2. 如果參數 ptr 是NULL指針,則函數什麼事都不做

  3. 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;
           
柔性數組優勢
  1. 友善記憶體釋放
  2. 有利于通路速度(

    連續的記憶體有益于提高通路速度,也有益于減少記憶體碎片

更深入了解 請參見陳皓大佬的>>C語言結構體裡的成員數組和指針<<

繼續閱讀