c語言動態分配記憶體及記憶體分配部分函式
#include<stdio.h>
/**
在C中動態分配記憶體的基本步驟有:
1,用malloc類的函式分配記憶體;
2,用這些記憶體支援應用程式
3,用free函式釋放記憶體
二、動態記憶體分配函式
malloc :從堆上分配記憶體
realloc : 在之前分配的記憶體塊的基礎上,將記憶體重新分配為更大或者更小的部分
calloc: 從堆上分配記憶體並清零
free:將記憶體塊返回堆
*/
void mainaa()
{
int *pi = (int*) malloc(sizeof(int));
*pi = 5;
printf("*pi:%d\n",*pi);
free(pi);
}
//為字串分配記憶體,將其初始化,並逐個字元列印字串,然而每次迭代name都會增加1,最後name會指向字串結尾的NUL字元,
//分配記憶體的起始地址丟失了
void mainbb()
{
//為10個雙精度浮點數分配空間,需要80個位元組
//double *pd = (double*)malloc(NUMBER_OF_DOUBLES*sizeof(double));
//以下程式只分配了10個位元組
const int NUMBER_OF_DOUBLES = 10;
double *pd = (double*)malloc(NUMBER_OF_DOUBLES);
//初始化靜態或全域性變數時不能呼叫函式,下面的程式碼宣告一個靜態變數,並試圖用
//malloc來初始化,這樣會產生一個編譯時錯誤訊息
//static int *pi = malloc(sizeof(int));
char *name = (char*)malloc(strlen("Susan")+1);
strcpy(name,"Susan");
while(*name != 0){
printf("%c",*name);
name++;
}
}
/**
使用calloc函式
calloc會在分配的同時清空記憶體,該函式的原型如下:void *calloc(size_t numElements,size_t elementSize);
calloc函式會根據numElements和elementSize兩個引數的乘積來分配記憶體,並返回一個指向記憶體的第一個位元組的指標。如果不能分配記憶體
則返回null,此函式最初用來輔助分配陣列記憶體。
如果numElements或elementSize為0,那麼calloc可能返回空指標。如果calloc無法分配記憶體就會返回空指標,而且全域性變數errno會設定為ENOMEM(記憶體不足),
這是POSIX錯誤碼,有的系統上可能沒有
**/
void maincc()
{
//下面兩端程式碼都是為pi分配了20位元組,全部包含0
//int *pi = calloc(5,sizeof(int));
//int *pi = malloc(5*sizeof(int));
//memset(pi,0,5*sizeof(int));
/**
memset函式會用某個值填充記憶體塊,第一個引數是指向要填充的緩衝區的指標,第二個引數是填緩衝區的值,最後一個引數是要填充的位元組數。
如果記憶體需要清零可以使用calloc,不過執行calloc可能比執行malloc慢。cfree函式已經沒用了。
*/
}
/**
realloc函式
realloc函式會重新分配記憶體,原型:void *realloc(void *ptr,size_t size);
realloc函式返回指向記憶體塊的指標。該函式接受兩個引數,第一個引數是指向原記憶體塊的指標,第二個是請求的大小。重新分配的塊大小和第一個引數
引用的塊大小不同。返回值是指向重新分配的記憶體的指標。
請求的大小可以比當前分配的位元組數小或者大。如果比當前分配的小,那麼多餘的記憶體會還給堆,不能保證多餘的記憶體會被清空。如果比當前分配的大,
那麼可能的話,就在緊挨著當前分配記憶體的區域分配新的記憶體,否則就會在堆的其他區域分配並把舊的記憶體複製到新區域。
如果大小是0而指標非空,那麼就釋放記憶體。如果無法分配空間,那麼原來的記憶體塊就保持不變,不過返回的指標是空指標,且errno會設定為ENMOEM,
**/
void maindd()
{
/**
下例使用兩個變數為字串分配記憶體。一開始分配16個位元組,但只用到了前面的13個位元組(12個十六進位制數字外加null結束字元(0))
*/
char *string1;
char *string2;
string1 = (char*)malloc(16);
strcpy(string1,"0123456789AB");
/**
緊接著,用realloc函式指定一個範圍更小的記憶體區域。然後列印這兩個變數的地址和內容
*/
string2 = realloc(string1,8);
printf("string1 value:%p [%s]\n",string1,string1);
printf("string2 value:%p [%s]\n",string2,string2);
}
/**
alloca函式和變長陣列
alloca函式(微軟為malloca)在函式的棧幀上分配記憶體。函式返回後會自動釋放記憶體。若低層的執行時系統不基於棧,
allocal函式會很難實現,所以這個函式時不標準的,如果應用程式需要可移植就儘量避免使用它。
C99引入了變長陣列(VLA),允許函式內部宣告和建立其長度由變數決定的陣列,比如:
void compute(int size){
char * buffer[size];
...
}
這意味著記憶體分配在執行時完成,且將記憶體作為棧幀的一部分來分配。另外,如果陣列用到sizeof操作符,也是在執行時而不是編譯時執行。
這麼做只會有一點小小的執行時開銷。而且一旦函式退出,立即釋放記憶體。因為我們沒有用malloc這類函式來建立陣列,所以不應該用free函式來
釋放它。alloca函式也不應該返回指向陣列所在記憶體的指標,但是可以解決。
VLA的長度不能改變,一經分配其長度就固定了。
**/
/**
動態記憶體分配技術
1,資源獲取即初始化
資源獲取即初始化(Resource Acquisition Is Initialization,RAII)是Bjarne Stroustrup發明的技術,可以用來解決C++中資源的分配和釋放。
即使有異常發生,這種技術也能保證資源的初始化和後續的釋放。分配的資源最終總是會得到釋放。
有好幾種方法可以在C中使用RAII。GNU編譯器提供了非標準的擴充套件來支援這個特性,通過演示如何在一個函式中分配記憶體然後釋放可以說明這種
擴充套件。一旦變數超出作用域會自動觸發釋放過程。
GNU的擴充套件需要用到RAII_VARIABLE巨集,它宣告一個變數,然後給變數關聯如下屬性
1,一個型別。
2,建立變數時執行的函式。
3,變數超出作用域時執行的函式。
這個巨集如下所示:
#define RAII_VARIABLE(vartype,varname,initval,dtor)\
void _dtor_ ## varname (vartype * v){dtor(*v);}\
vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
在下例中,我們將name變數宣告為字元指標。建立它時會執行malloc函式,為其分配32位元組。當函式結束時,name超出作用域就會執行free函式:
void raiiExample(){
RAII_VARIABLE(char*,name,(char*)malloc(32),free);
strcpy(name,"RAII Example");
printf("%s\n",name);
}
函式執行後會列印"RAII_Example"字串。不用GNU擴充套件也可以達到類似效果。
2、使用異常處理函式
另外一種處理記憶體釋放的方法是利用異常處理。儘管異常處理不屬於標準C,但如果可以使用它且不考慮移植問題,它會很有用。下面說明利用
Microsoft Visua Studio版的C語言的方法。
這裡的try塊包含任何可能在執行時丟擲異常的語句。不管有沒有異常丟擲,都會執行finally塊,因此也一定會執行free函式。
void exceptionExample(){
int *pi = NULL;
__try{
pi = (int*)malloc(sizeof(int));
*pi = 5;
printf("%d\n",*pi);
}
__finally{
free(pi);
}
}
*/