1. 程式人生 > >在子函式中malloc分配記憶體和free釋放記憶體的方法(基於C)

在子函式中malloc分配記憶體和free釋放記憶體的方法(基於C)

1. 子函式malloc分配記憶體

       為了增強程式可讀性,有時會在子函式中malloc分配記憶體。測試瞭如下三種方法,容易想到的是第一種。事實證明這種也是錯誤的!

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct _dataStc
{
	int buf[100];
	int size;
}dataStc;

void func1(dataStc* p)
{
	p = (dataStc*)malloc(sizeof(dataStc));
	p->size = 10;
	return;
}

dataStc* func2(void)
{
	dataStc* p;
	p = (dataStc*)malloc(sizeof(dataStc));
	p->size = 10;
	return p;
}

void func3(dataStc** p)
{
	*p = (dataStc*)malloc(sizeof(dataStc));
	(*p)->size = 10;
	return;
}

int main(int argc, const char * argv[]) {
	
	dataStc *p1 = NULL;
	dataStc *p2 = NULL;
	dataStc *p3 = NULL;

	func1(p1);
	p2 = func2();
	func3(&p3);

	if (p1 == NULL)
	{
		printf("p1=NULL\n");
	}
	else
	{
		printf("p1->size:%d\n", p1->size);
	}
	if (p2 == NULL)
	{
		printf("p2=NULL\n");
	}
	else
	{
		printf("p2->size:%d\n", p2->size);
	}
	if (p3 == NULL)
	{
		printf("p3=NULL\n");
	}
	else
	{
		printf("p3->size:%d\n", p3->size);
	}
	return 0;
}

執行結果如下所示:

p1=NULL
p2->size:10
p3->size:10

結論:只有後兩種方法能正確分配記憶體。即直接返回記憶體指標,或將二級指標作為引數傳入子函式。這是因為,在子函式中,記憶體指標只是被當做一個變數來處理的(雖然這個變數被定義為dataStc *),對其賦值後再返回,它的值當然沒改變。

另外,由於成員選擇運算子“->”的優先順序比取值運算子“*”高,所以在func3中,*p需要加括號。

2. 子函式free釋放記憶體

在子函式中釋放記憶體,很容易想到的是寫個下面這樣的函式釋放記憶體:

void func_free(dataStc* p)
{
	free(p);
	return;
}

上面的函式能釋放記憶體,但也有個缺陷。因為C在free一段記憶體後,該記憶體的確是被釋放了(可以被其他程式使用),但指標p仍然指向該段記憶體(而不是我們期待的NULL)。這時p就成為野指標了(不同於空指標),仍然可以訪問或修改p指向的記憶體。如果其他程式不慎又通過p訪問了記憶體,程式或許不報錯,但可能帶來不可預知的後果。

因此,保險做法是,在free後,將p賦為NULL。由於這裡又將輸入變數p修改了,因此仍然需要傳遞二級指標。正確的程式碼為:

void func_free(dataStc** p)
{
	free(*p);
	*p = NULL;
	return;
}

這樣,在其他程式中訪問p時,就可先判斷其是否為NULL了。