C語言-函數指針與函數名的區別***

 

        記得大學時老師曾說函數的函數名是函數的入口的指針,之前看block通過clang編譯生成的C代碼發現很多函數指針,於是想了解函數指針與函數名有什麼區別?以及函數指針一般都有些什麼作用。

 

函數指針與函數名的區別

首先先定義一函數以及一個指向蓋函數的函數指針,並分別對他們進行調用。

 

//VS 2017
void fun(int& x);
//定義函數fun  
void fun(int& x) {
	x++;
	printf("%i\n", x);
}

typedef void(*FunP)(int& n);
void  fnt(FunP fn, int& n)
{
	fn(n);
}

int main()
{
#if 1
    void(*funPattr[])(int&);//聲明函數指針數組funPattr
	void(*funP)(int&);//聲明函數指針funP  
	funP = fun;

	int x = 0;
	printf("&fun=%p\n", &fun);
	printf("fun=%p\n", fun);
	printf("*fun=%p\n", *fun);
	(&fun)(x);
	fun(x);
	(*fun)(x);

	printf("&funP=%p\n", &funP);
	printf("funP=%p\n", funP);
	printf("*funP=%p\n", *funP);
	//(&funP)(x);
	funP(x);
	(*funP)(x);

	fnt(fun, x);
	fnt(&fun, x);

	fnt(funP, x);

#endif

	system("pause");
	return 0;
}

輸出結果爲

    正如我們平常使用的那樣,1如何2正常輸出了。爲了確認函數名是否等價於函數指針,於是把函數名以及函數指針的調用方式換了一下。發現(* <函數名>)()的的形式也能進行調用並正常輸出,而函數指針直接調用也沒有問題。

    這裏暫時得出兩個結論

    1、函數名的使用基本等價於函數指針,函數名、取地址&、取內容*得到的都是函數的地址

    2、函數名也可以(* <函數名>)()來調用,只是這種方法讀寫都不方便,所以被簡化了

    得到一個問題是:爲什麼使用“funP = &fun”的形式對funP賦值,而不直接使用“funP = fun”

對於上面得出的問題,我們試着直接輸出funP與fun作爲指針的值,進行比較。

       首先,所有結果都正常打印了(似乎fun真的像一個指針)。

       其次fun是一個指向自己的指針。根據大家常說的fun作爲函數的入口的依據,那麼它的地址就是函數入口的地址,其次我們通過fun來找到函數,那麼他就應該指向函數的入口。這麼解釋似乎能說的通爲什麼fun是一個指向自己的指針。雖然感覺有點怪怪的。

       經過上面兩端代碼後我們好像能確定函數名就是函數指針,那我們看看作爲函數指針,它能否做其他函數指針能做的事,比如賦值。

第一種:函數名與FunP函數指針都是函數指針。fun是一個函數指針常量,funP是一個函數數指針變量。

        雖然通過常量與變量來解釋函數名無法賦值可以幫助理解,但是我們發現對fun賦值時編譯器給的錯誤提示並不是說對常量進行賦值,而是告訴我們=號兩端格式不匹配。對此,第二種理解更合理。

 

第二種:函數名和數組名實際上都不是指針,但是,在使用時可以退化成指針,即編譯器可以幫助我們實現自動的轉換。

        這也可以解釋爲什麼當我們在=號右側使用函數名時,無論是取值還是取地址都沒有問題,因爲編譯替我們做了相當於強制類型轉換的工作,而在當函數名在=號左側時,右側的函數指針並沒有這個功能,畢竟他們倆不是同一種結構。

函數指針的作用

           當我無法區別函數名與函數指針時,我很好奇既然函數名也是函數指針類型,那爲什麼不直接使用函數名。提出函數指針的目的是什麼,它有什麼作用。雖然現在明白了函數名不等於函數指針,但是問題還是要解決。

1、作爲變量傳遞,可稱爲參數

       既然函數指針如同別的指針變量一樣通過*來獲得,那麼函數指針作爲變量,自然可以進行賦值,取值操作,可以作爲函數的參數進行傳遞。普通指針變量能做什麼它就能做什麼。

2、優化函數調用,封裝

      通常函數名的命名都是見名知意,直接用函數名調用可讀性自然要好,但如果是不想給別人查看的代碼,被人破解後通過函數名直接就瞭解具體函數作用與函數調用,而函數指針可以作爲函數的一層外衣,提供一定的保護作用。

      其次通過函數指針來調用函數,可以起到一定的封裝效果,函數指針作爲引用層(中層),函數作爲實現層(底層),便於分層設計,函數指針可以爲上層用戶提供統一藉口,便於系統抽象各個功能或操作,降低程序耦合度。如

fopen就是個例子,他可以打開文件。而C裏面將磁盤文件、串口、USB等諸多設備抽象爲文件。
爲C++實現多態性的虛函數表也是通過函數指針實現。
3、回調函數
  這是最重要的使用場景了,回調函數就是一個通過函數指針調用的函數。
回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,
用於對該事件或條件進行響應。舉個簡單的例子。
如去幹洗店洗衣服,我們通常會留下電話號碼,而乾洗店洗好衣服後就會通過電話通知我們,
讓我們來取衣服。這個場景裏電話號碼就是回調函數,相當於函數指針。
這在實現通知機制的時候就能看到。其次在我們編寫一個對一般數據類型進行操作的庫時,
爲了能讓庫可用於多種數據類型(int、float、string),也可以使用函數指針,並進行回調。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章