malloc,calloc,realloc及動態開闢記憶體常見錯誤

audience_fzn發表於2018-08-04

1.為什麼存在動態記憶體管理?

我們已經掌握的記憶體開闢方式有:

int val = 20;//在棧空間上開闢四個位元組

char arr[10] = {0}; //在棧空間上開闢10個位元組的連續空間

但是這些開闢空間的方式開闢的空間大小是固定的,陣列在宣告的時候,也是必須指定大小。他們所需要的記憶體在編譯時分配

而有些時候我們需要的空間大小在程式執行的時候才能知道,所以就要有一種動態記憶體分配的方式

動態開闢的記憶體都在堆上

2.如何動態的分配記憶體?

c語言提供了一個動態記憶體開闢的方式:

  • malloc:向記憶體動態的申請一塊空間
  • 函式原型: void * malloc (size_t size);
  • 函式說明:

1)這個函式向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指標

2)如果開闢成功,則返回一個指向開闢好空間的指標

3)如果開闢失敗,則返回一個NULL指標,因此malloc的返回值一定要做檢查

4)返回值型別是void* ,所以malloc函式並不知道開闢空間的型別,具體在使用的時候由使用者自己來決定。

5)如果引數size為0,malloc的行為是標準為定義的,取決於編譯器

 

c語言提供了另一個函式free,專門用來做動態記憶體的釋放和回收的

  • free:用來釋放動態開闢的記憶體
  • 函式原型: void free(void* prt);
  • 函式說明:

1)如果引數ptr指向的空間不是動態開闢的,那free函式的行為是未定義的

2)如果引數ptr是NULL指標,則函式什麼事都不做

malloc和free都宣告在標頭檔案#include<stdlib.h>中

int main()
{
	int *ptr = NULL;
	ptr = (int*)malloc(10*sizeof(int));
	if (ptr != NULL)//一定要檢查是否為空
	{
		printf("haha\n");
	}
	free(ptr);//一定要記得釋放
	ptr = NULL;
        return 0;
}

//使用時要判斷ptr是否為空,動態申請的記憶體一定要使用free()釋放,並且置空,否則可能引起記憶體洩漏的問題

 

  • calloc:用來動態開闢記憶體,不同於malloc的是,它會進行初始化
  • 函式原型:void* calloc(size_t num,size_t size);
  • 函式說明:

1)calloc函式的功能是為num個大小為size的元素開闢一塊空間,並且把空間的每個位元組都初始化為0

2)與函式malloc的區別在於calloc會在返回地址之前把申請的所有空間的每個位元組都初始化為0

int main()
{
	int *ptr = NULL;
	ptr = (int*)calloc(10, sizeof(int));
	if (NULL != ptr)
	{
		printf("haha\n");
	}
	free(ptr);
	ptr = NULL;
        return 0;
}
  • realloc:可以對動態開闢的記憶體大小進行調整,realloc函式的出現使動態記憶體管理更加靈活,有時候我們申請的空間太大,或者太小了,就可以使用realloc對其進行調整
  • 函式原型:void* realloc(void* ptr,size_t size);
  • 函式說明:

1)ptr是要調整的記憶體地址

2)size是調整之後的新大小

3)返回值為調整直觀的記憶體起始位置

4)這個函式在調整原記憶體空間大小的基礎上,還會將原來記憶體中的資料移動帶新的空間

 

  • realloc函式在調整記憶體空間存的時候在倆種情況

1)原有空間之後有足夠大的空間

2)原有空間之後沒有足夠的空間

int main()
{
	int *ptr = (int*)malloc(10 * sizeof(int));
	int * newptr = NULL;
	newptr = (int*)realloc(ptr, 100);
	if (NULL != newptr)
	{
		printf("haha");
	}
	free(newptr);
        return 0;
}

3.常見的動態記憶體錯誤

  • 對NULL指標進行解引用操作
     void test()
    {
        int * ptr = (int*)malloc(10*sizeof(int));
        *ptr = 20;
        free(ptr);
    }

    //如果開闢記憶體失敗,ptr=NULL,對空指標進行解引用會出錯,所以使用前要先判斷ptr是否為NULL

  • 對動態開闢空間的越界訪問
void test()
{
	int i = 0;
	int *p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//當i=10的時候越界訪問
	}
	free(p);
}
  • 對非動態開闢記憶體的釋放,棧上申請的空間,不需要用free釋放
  • 使用free釋放一塊動態開闢記憶體的一部分
    void test()
    {
    	int *p = (int*)malloc(100);
    	p++;
    	free(p);
    }

    //p++後不在指向動態記憶體的起始位置,malloc申請,釋放的空間都是整體的,不能區域性釋放

  • 對一塊動態記憶體釋放多次
  • 動態開闢的空間忘記釋放(記憶體洩漏),切記動態開闢的空間一定要釋放,並且要正確的釋放

相關文章