相信大家當初學習C語言的時候,老師一定跟你說過這樣的一句大實話:【**指針,是C語言的靈魂**】。
? 筆者自出來工作以來,幾乎天天都要跟C語言打交道,回過頭來想一想,這話確實沒有錯。
? 本文,打算從一個另類的角度,介紹下C語言指針的高級用法,通過本文的閱讀,你將了解到以下知識:
- C語言的指針是什么?
- C語言指針的高級應用:函數指針
- 函數指針的具體應用示例
C語言的指針是什么
? 指針是什么?相信所有的C語言教程,都會告訴你:【指針就是地址】。沒錯,的確是這么回事。C語言的指針就好比房屋的地址,只要有了地址,我們就可以訪問到全世界的每一個角落。C語言的世界也是如此,地址就是一切,有了地址,就沒有干不成的事情。
? 在C語言里,如果想對一個int類型的變量a進行賦值操作,我們會這樣寫: a = 5;這樣的形式,就是直接訪問。對應的,我們有間接訪問的方式,就是通過指針來實現。比如我們可以定義一個指針 int *b = &a; 指針b存放的是a變量的地址,我們通過這樣: *b = 5;一樣可以實現對a進行賦值操作,這就是間接訪問的力量。
? C語言的指針是靈活的,它不僅可以像如上代碼一樣,指向一個普通變量,它也可以指向一個結構體變量,甚至還可以指向一個函數名。原因就在于,函數名,在C語言的語法里,本質上就代表了函數的執行地址,說白了,它也是一個“指針”。而這,就是我們以下要詳細介紹的【函數指針】。
C語言指針的高級應用:函數指針
? 【函數指針】,顧名思義,就是一個指向函數的指針,它的本質還是一個指針,只不過這個指針指向的內容是一個函數。
? 在講解【函數指針】之前,我們先假設有若干個函數,它們的原型定義如下所示:
int test_function_1(int arg);
int test_function_2(int arg);
int test_function_3(int arg);
...
int test_function_n(int arg);
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NgxkHoX2-1661923373249)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
? 從函數原型上我們可以知道,這些個函數都是接收一個int類型的形參,返回值類型為int型。從原型上看,這幾個函數幾乎是一模一樣,那么我們有沒有方法可以將這些原型一致的函數重新整理定義呢?答案肯定是,有的。
? 追求高效、簡潔的C語言就我們提供了一個非常有用的關鍵字typedef,通過typedef我們可以重新創造出一個新的數據類型,而不再局限于C語言的基本數據類型。比如我們就可以利用typedef定義一個叫形如上述函數原型的【函數指針】數據類型,它的寫法如下所示:
typedef int (*FUNCTION)(int arg);
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MLEkQ9Bo-1661923373252)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
? 從定義上看,因為()擁有最高優先級,所以*FUNCTION首先結合在一起,這就決定了它是一個指針。接著,*FUNCTION的后面接上了(int arg),這就是函數的入參;而前面的int 就表示函數的返回值。這就是【函數指針】的原型定義。
? 【函數指針】最為一種特殊的指針,自然它也是要指向內容才能使用的,毫無疑問,它就是要指向對應原型的函數。具體怎么使用呢?
函數指針的具體應用示例
? 【函數指針】這種高階用法,可能有些人用得比較少,但是如果你閱讀過類似openssl這樣的大型C語言編寫的開源代碼之后,相信你一定會感嘆:原來,C語言的指針還能這么用!!!
? 有了【函數指針】的利器,我們就可以用它來大做文章,請看以下示例代碼:
int test_function_1(int arg)
{
printf("This msg is printed form %s ...\n", __func__);
return arg;
}
int test_function_2(int arg)
{
printf("This msg is printed form %s ...\n", __func__);
return arg;
}
int test_function_3(int arg)
{
printf("This msg is printed form %s ...\n", __func__);
return arg;
}
typedef int (*FUNCTION)(int arg);
int function_pointer_test_1(void)
{
int ret;
int arg = 1;
FUNCTION func = NULL; //定義個函數指針
func = test_function_1; //把函數指針指向test-function-1
//ret = test_function_1(arg); //通過函數名直接調用test-function-1函數
ret = func(arg); //通過函數指針間接調用test-function-1函數
func = test_function_2; //把函數指針指向test-function-2
//ret = test_function_2(arg); //通過函數名直接調用test-function-2函數
ret = func(arg); //通過函數指針間接調用test-function-2函數
func = test_function_3; //把函數指針指向test-function-3
//ret = test_function_3(arg); //通過函數名直接調用test-function-3函數
ret = func(arg); //通過函數指針間接調用test-function-3函數
return 0;
}
int function_pointer_test_2(void)
{
int ret;
int arg = 1;
int i = 0;
FUNCTION func = NULL; //定義個函數指針
FUNCTION func_array[] = //定義一組函數列表
{
test_function_1,
test_function_2,
test_function_3,
};
//終極大招,循環處理3個函數的間接調用
for (i = 0; i < sizeof(func_array); i ++) {
func = func_array[i]; //把函數指針指向對應的函數
ret = func(arg); //通過函數指針間接調用對應的函數
}
return 0;
}
int main(int argc, char **argv)
{
function_pointer_test_1();
function_pointer_test_2();
/*
結果輸出,兩個function_pointer_test函數均有同樣的輸出結果:
This msg is printed form test_function_1 ...
This msg is printed form test_function_2 ...
This msg is printed form test_function_3 ...
*/
return 0;
}
/*
總結:
雖然通過兩個調用的方式,輸出結果是一致的,但是顯然方式2的處理更為高效、簡潔;
從代碼的字里行間,仿佛看到C語言的“多態”:將不同的函數名賦值給同一個函數指針變量;
使用同一個函數指針發起函數調用,得到不一樣的結果輸出,這不就是多態嗎?
*/
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oLznHZnb-1661923373260)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
? 通過如上的示例代碼,C語言的【多態】你get到了嗎?
? 千萬不要小瞧了上面的示例代碼,如果你能熟練掌握其中的【指針精髓】,再去閱讀一些業內廣泛使用的C語言編寫的開源項目源碼,比如大名鼎鼎的openssl;相信閱讀之后,你的C語言功力一定會大大地提升。
? 不過,值得注意的是,文中的示例代碼均是筆者在非編程環境下編寫,屬于【白板編程】,難免會出現編寫錯誤、編譯不過、或執行結果不正確的情況;還請細心的讀者誠心指正,感激不盡。
延伸閱讀預告:
為充實C語言的指針知識,筆者將會在后續的文章中整理大名鼎鼎的C語言巨著《C和指針》,敬請期待。
-
C語言
+關注
關注
180文章
7604瀏覽量
136692 -
指針
+關注
關注
1文章
480瀏覽量
70551 -
函數指針
+關注
關注
2文章
56瀏覽量
3778
發布評論請先 登錄
相關推薦
評論