1. 程式人生 > >理解 (*(void (*)())0)()

理解 (*(void (*)())0)()

1、鋪墊

在分析上面的語句前,我們先從簡單的入手。先來區別和理解下面這兩個定義。

float  *g();   和 float  (*h) ();

直接上答案:由於( )結合的優先級別高於*,所以g先和後面的( )結合,構成一個函式,該函式的返回值是一個指向float 數的指標。同理,h是一個函式指標,它所指向的函式的返回值是一個浮點數。

當我們知道如何宣告一個給定型別的變數,那麼不難得出該型別的型別轉換:只需要把宣告中的變數名和宣告末尾的分號去掉,然後將剩下的部分用一個括號整個“封裝”起來。最簡單的例子比方說我們以前學過的強制轉換   (int) value這類的,但是我們現在稍微複雜一些。如下:

 float  (*h) ();   表示h是一個指向返回值為浮點型別的函式的指標,所以有

(float  (*) () )   表示一個“指向返回值為浮點型別的函式的指標”的型別轉換  (這一步需要理解一下)

2、理解

有了上面的鋪墊之後,我們就開始理解(*(void (*)())0)();這個語句。

1) (void (*)())  裡面的這個和鋪墊的一樣,是一個型別轉換,表示一個“指向返回值為void型別的函式的指標”的型別轉換。

2)   (void (*)())  0  這是表示將常數0轉換為“指向返回值為void的函式指標”型別。

3)    有了1) 和 2)的理解之後,我們就明白,0是一個函式指標,它指向的函式的返回值型別為void,這樣就比較好辦了,那我們就按照使用指標變數的方法去理解它。我們使用一個指標變數的時候,前面會帶一個*號,同理,對於這樣一個函式指標,我們在呼叫它的時候。也類似地這麼呼叫,就是(*(void (*)())0)();這就分析完了。

3、補充

這個例子主要運用在計算機地址方面,比方說arm晶片一上電的時候,是從0地址處開始執行程式。而要對各個狀態字進行配置或者存入某個記憶體塊的時候,處理的方法也都是把一個常數(也就是這個地址)轉換成一個指標型別,然後對這個指標進行操作就能對該地址進行操作,這方面在嵌入式的配置方面見得比較多。

之前有一個功能挺複雜的驅動程式,其中有一條語句是這樣的:

pos == IOFPGA_DO_DATA_ADDR(iofpga_if_get_dod_addr(), pfifo) ;

那我就進去看一下它的定義咯,如下:

#define IOFPGA_DO_DATA_ADDR(base, reg)((unsigned int)&(((struct st_iofpga_do_data *)((int)base))->reg))

右邊的表示式我一看就知道在幹嘛了(因為在此之前我看過標題的例子),我們看一下右邊的表示式對這base和reg這兩個形參做了什麼事情,很明顯嘛:

1)首先把base強制轉換為int型別,注意,它是一個常數。

2)結合上面的例子分析,(struct st_iofpga_do_data *)((int)base) 和上面的例子用法一樣,就是把一個常數轉換成一個指標,這      裡是轉換成自定義的結構體指標。

3)((struct st_iofpga_do_data *)((int)base))->reg)  把這個常數轉換成結構體指標之後,在這個結構體中找到傳入的reg成員。

4)  &(((struct st_iofpga_do_data *)((int)base))->reg))  找到該結構體裡面的這個成員之後,用&取其地址

5) (unsigned int)&(((struct st_iofpga_do_data *)((int)base))->reg)  &取到這個成員的地址之後,強制轉換為(unsigned int)型別

6)分析完畢....

結合對結構體的定義和傳入的實參,不難理解這個函式的功能就是找到結構體特定成員的偏移地址。