1. 動態記憶體分配
先來介紹三個動態記憶體分配的函數:malloc,calloc和realloc。說來慚愧,以前只知道malloc。現在來看下他們的區別:
malloc:最常用的分配記憶體塊,但是不對記憶體進行初始化。
calloc:分配記憶體塊,但是對記憶體塊進行清零操作,這就造成此函數的效率要比malloc要低。
realloc:調整(增加或者減少)之前分配記憶體塊的大小。
由於上面的函數只是開闢了一段記憶體,因此無法知道你要利用這段記憶體來儲存什麼類型的資料,因此只是返回一個void *類型的值,當然,void *可以和任何指標類型互相轉換。
當分配記憶體失敗時(可能是記憶體不足或者其他原因),以上的函數都會返回一個null 指標,那麼我們安全期間,在我們使用這塊分配的記憶體前,都應該進行一次null 指標的驗證。
int main (void){ int *p; p=(int *)malloc(sizeof(*p)*1000); if(p==NULL) { exit(EXIT_FAILURE); } printf("success"); return 0;}
注意上面的兩點,一個是null 指標的驗證,另外一個是分配記憶體的大小,這裡我們常常是用類型的大小乘以儲存成員的數量來計算分配。如果是字串,我們就不要忘記了加1,來儲存\0。
#include <stdio.h>#include <stdlib.h>int main (void){ char *p; p=(char *)malloc(sizeof(*p)*1000+1); if(p==NULL) { exit(EXIT_FAILURE); } printf("success"); return 0;}
接下來我們看一下calloc元素的原型:
void * __cdecl calloc(_In_ size_t _Count, _In_ size_t _Size);
從上面我們可以看到calloc函數有兩個參數,分配是,數量和大小。由此可以說明,calloc是C語言用來分配數組空間最好的選擇。那麼我們就把第一段代碼改一下:
int main (void){ int *p; p=(int *)calloc(1000,sizeof(int)); if(p==NULL) { exit(EXIT_FAILURE); } printf("success"); return 0;}
這樣就更合適了。
最後是realloc,我們也來看一下realloc元素的原型:
void * __cdecl realloc(_Post_ptr_invalid_ void * _Memory, _In_ size_t _NewSize);
當我們之前分配了一個數組的大小,但是後來我們卻發現這個大小不夠用了,或者是太大了,那麼我們就可以利用realloc來調整我們的佔用記憶體大小:
int main (void){ int *p; p=(int *)calloc(1000,sizeof(int)); if(p==NULL) { exit(EXIT_FAILURE); } realloc(p,sizeof(int)*100); if(p==NULL) { exit(EXIT_FAILURE); } printf("success"); return 0;}
也別忘了檢驗p是否為空白指標的情況。
在C標準中,並沒有對realloc的實現做以規定,但是對於大部分編譯器來說,如果是把原地址空間縮小,他會盡量地不去移動原來的資料。如果是把空間增大,那麼他會盡量首先在原地址的末尾去分配記憶體,如果不足以分配,那麼編譯器才會去尋找新的地址塊,並且把原地址空間內的資料轉移到新的地址上。
2. 釋放空間
習慣了Java/C#的我們,似乎已經忘記了要回收垃圾的習慣,在C/C++中,是沒有GC的,因此我們要記得,當我們在堆上分配了一塊記憶體,並且不在使用時,我們要使用free函數來釋放掉空間。看下free的原型:
void __cdecl free(_Post_ptr_invalid_ void * _Memory);
很簡單,不再贅述。
3. 指向指標的指標
在讀大學時,我一直對這個概念不是很理解,現在我更願意這樣去理解指標。
當我們聲明了int *p=malloc(1000)的時候,我們可以這樣來理解:
其實我更願意把p就理解成一個地址的值,p=0x1111(0x1111是分配的1000位元組記憶體的首地址)。那麼什麼是指向指標的指標呢?
這裡的q就是指向指標的指標,q的值就是0x0004,也就是p所在的地址。
以此類推,我們還可以知道指向指標的指標的指標。
4. 函數指標
我們來看C語言裡提供了qsort函數:
_CRTIMP void __cdecl qsort(_Inout_bytecap_x_(_NumOfElements * _SizeOfElements) void * _Base, _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *));
最後一個參數就是一個函數指標,其實不用這麼麻煩,我們來看個簡單的函數指標的原型:
double (*function)(int);
這個就是最簡單的函數指標的原型,與返回指標類型的函數相比,他們相差的只是*和函數名之間要用括弧括起來。
當傳進來一個函數指標時,我們便可以在函數中適用這個傳進來的參數(函數指標)了。例如在qsort裡,我們便可以自己制定比較規則,不再多說。