void *表示一個「不知道類型」的指針,也就不知道從這個指針地址開始多少字節爲一個數據。和用int表示指針殊途同歸,只是更明確是「指針」。編程
所以void*只能表示一個地址,不能用來&取值,也不能++--移動指針,所以不知道多少字節是一個數據單位。數組
int nums[] = {3,5,6,7,9}; void* ptr1 = nums; //int i = *ptr1; // 對於void指針無法直接取值 int* ptr2 = (int*)nums; printf("%d,%d\n",ptr1,ptr2); int i = *ptr2; printf("%d\n",i);
從輸出結果能夠看出,不管是無類型的void指針仍是int類型指針,指向的地址都是同樣的:函數
PS:void *就是一個不能動的「地址」,在進行&、移動指針以前必須轉型爲類型指針。spa
這裏咱們看一下咱們以前瞭解的memset函數,其第一個參數就是一個void指針,它能夠幫咱們屏蔽各類不一樣類型指針的差別。以下面代碼所示,咱們既能夠傳入一個int類型數組的指針,也能夠傳入一個char類型數組的指針:指針
int nums[20]; memset(nums,0,sizeof(nums)); char chs[2]; memset(chs,0,sizeof(chs));
那麼,咱們也能夠試着本身動手模擬一下這個memset函數,暫且命名爲mymemset吧:code
void mymemset(void *data,int num,int byteSize) { // char就是一個字節,而計算機中是以字節爲單位存儲的 char *ptr = (char*)data; int i; for(i=0;i<byteSize;i++) { *ptr=num; ptr++; } } int main(int argc, char *argv[]) { int nums[20]; mymemset(nums,0,sizeof(nums)); int i,len=sizeof(nums)/sizeof(int); for(i=0;i<len;i++) { printf("%d ",nums[i]); } printf("\n"); return 0; }
在這個mymemset函數中,咱們利用void指針接收不一樣類型的指針,利用char類型(一個字節)逐個字節讀取內存中的每個字節,最後依次填充指定的數字。因爲char類型是一個具體類型,因此可使用++或者--進行指針的移動。blog
對於結構體類型,也可使用咱們的mymemset函數:排序
typedef struct _Person { char *name; int age; } Person; Person p1; mymemset(&p1,0,sizeof(Person)); printf("p1.Age:%d\n",p1.age);
最終的運行結果以下圖所示:接口
void *的用途:在只知道內存,可是不知道是什麼類型的時候。內存
我想用過.NET中的委託的童鞋,對於函數指針應該不會陌生,它是委託的原型。函數指針是一個指向函數的指針,咱們能夠在C中輕鬆地定義一個函數指針:
typedef void (*intFunc)(int i);
這裏咱們定義了一個無返回值的,只有一個int類型參數的函數指針intFunc。咱們能夠在main函數中使用這個函數指針來指向一個具體的函數(這個具體的函數定義須要和函數指針的定義一致):
// 聲明一個intFunc類型的函數指針 intFunc f1 = test1; // 執行f1函數指針所指向的代碼區 f1(8);
這裏test1函數的定義以下:
void test1(int age) { printf("test1:%d\n",age); }
最終運行結果以下圖所示,執行函數指針f1即執行了其所指向的具體的函數:
這裏咱們經過一個小案例來對函數指針作一個基本的使用介紹。相信大部分的C#或Java碼農都很熟悉foreach,那麼咱們就來模擬foreach對int數組中的值進行不一樣的處理。具體體現爲for循環的代碼是複用的,可是怎麼處理這些數據不肯定,所以把處理數據的邏輯由函數指針指定。
void foreachNums(int *nums,int len,intFunc func) { int i; for(i=0;i<len;i++) { int num = nums[i]; func(num); } } void printNum(int num) { printf("value=%d\n",num); }
在foreachNums函數中,咱們定義了一個intFunc函數指針,printNum函數是知足intFunc定義的一個具體的函數。下面咱們在main函數中將printNum函數做爲函數指針傳遞給foreachNums函數。
int nums[] = { 1,5,666,23423,223 }; foreachNums(nums,sizeof(nums)/sizeof(int),printNum);
最終運行的結果以下圖所示:
經過函數指針,咱們能夠屏蔽各類具體處理方法的不一樣,也就是將不肯定的因素都依賴於抽象,這也是面向抽象或面向接口編程的精髓。
(1)定義函數指針及getMax主體:
typedef int (*compareFunc)(void *data1,void *data2); // getMax 函數參數說明: // data 待比較數據數組的首地址,uniteSize單元字節個數 // length:數據的長度。{1,3,5,6}:length=4 // 比較data1和data2指向的數據作比較, // 若是data1>data2,則返回正數 void *getMax(void *data,int unitSize,int length,compareFunc func) { int i; char *ptr = (char*)data; char *max = ptr; for(i=1;i<length;i++) { char *item = ptr+i*unitSize; //到底取幾個字節進行比較是func內部的事情 if(func(item,max)>0) { max = item; } } return max; }
這裏能夠看到,在getMax中到底取幾個字節去比較都是由compareFunc所指向的函數去作,getMax根本不用關心。
(2)定義符合函數指針定義的不一樣類型的函數:
int intDataCompare(void *data1,void *data2) { int *ptr1 = (int*)data1; int *ptr2 = (int*)data2; int i1=*ptr1; int i2=*ptr2; return i1-i2; } typedef struct _Dog { char *name; int age; } Dog; int dogDataCompare(void *data1,void *data2) { Dog *dog1 = (Dog*)data1; Dog *dog2 = (Dog*)data2; return (dog1->age)-(dog2->age); }
(3)在main函數中針對int類型和結構體類型進行調用:
int main(int argc, char *argv[]) { // test1:int類型求最大值 int nums[] = { 3,5,8,7,6 }; int *pMax = (int *)getMax(nums,sizeof(int),sizeof(nums)/sizeof(int), intDataCompare); int max = *pMax; printf("%d\n",max); // test2:結構體類型求最大值 Dog dogs[] ={{"沙皮",3},{"臘腸",10},{"哈士奇",5}, {"京巴",8},{"大狗",2}}; Dog *pDog = (Dog *)getMax(dogs,sizeof(Dog), sizeof(dogs)/sizeof(Dog),dogDataCompare); printf("%s=%d",pDog->name,pDog->age); return 0; }
最終運行結果以下圖所示:
qsort包含在<stdlib.h>頭文件中,此函數根據你給的比較條件進行快速排序,經過指針移動實現排序。排序以後的結果仍然放在原數組中。使用qsort函數必須本身寫一個比較函數。咱們能夠看看qsort函數的原型:
void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );
能夠看出,qsort的第四個參數就是一個函數指針!其所指向的函數應該是一個返回值爲int類型的,參數爲兩個void指針。那麼,咱們可使用上面咱們已經寫好的兩個compare方法做爲參數傳入qsort來對上面的int數組和結構體數組進行快速排序。
int nums[] = { 3,5,8,7,6 }; qsort(nums,sizeof(nums)/sizeof(int),sizeof(int),intDataCompare); int i; for(i=0;i<sizeof(nums)/sizeof(int);i++) { printf("%d ",nums[i]); } printf("\n"); Dog dogs[] ={{"沙皮",3},{"臘腸",10},{"哈士奇",5}, {"京巴",8},{"大狗",2}}; qsort(dogs,sizeof(dogs)/sizeof(Dog),sizeof(Dog),dogDataCompare); for(i=0;i<sizeof(dogs)/sizeof(Dog);i++) { printf("%s %d ",dogs[i].name,dogs[i].age); }
那麼,快速排序後是否有結果呢?答案是確定的,咱們能夠傳入各類比較方法,能夠升序排序也能夠降序排序。
如鵬網,《C語言也能幹大事(第三版)》