[英]Can you cast a “pointer to a function pointer” to void*
通過評論我的回答啟發這里 。
這一系列步驟在C標准(C11)中是否合法?
void*
void*
執行指針算術void*
或者相當於代碼:
void foo(void) { ... }
void bar(void) { ... }
typedef void (*voidfunc)(void);
voidfunc array[] = {foo, bar}; // Step 1
void *ptr1 = array; // Step 2
void *ptr2 = (char*)ptr1 + sizeof(voidfunc); // Step 3
voidfunc bar_ptr = *(voidfunc*)ptr2; // Step 4
我認為這是允許的,因為實際的函數指針只能通過正確鍵入的指針訪問。 但Andrew Henle指出, 標准部分6.3.2.3似乎沒有涵蓋這一點:指針 。
你的代碼是正確的。
指向函數的指針是一個對象,並且您正在向對象 (指向函數指針的指針)轉換指針以void
指針void
並再次返回; 然后最終取消引用指向對象的指針。
至於char
指針算術,這由C11的腳注106引用:
106)接近指針運算的另一種方法是首先將指針轉換為字符指針:在此方案中,首先將轉換后的指針中添加或減去的整數表達式乘以最初指向的對象的大小。 ,並將結果指針轉換回原始類型。 對於指針減法,字符指針之間差異的結果類似地除以最初指向的對象的大小。 當以這種方式查看時,實現僅需要在對象結束之后提供一個額外字節(其可以與程序中的另一個對象重疊)以滿足“超過最后一個元素”的要求。
是的,代碼很好。 這里有各種陷阱和轉換規則:
void*
是指向對象類型的指針的通用指針類型。 任何指向對象類型的指針都可以隱式轉換為void*
。 (C176.3.2.3§1)。 void*
,反之亦然。 (C176.3.2.3§1) void(*)(void)
作為泛型函數指針類型。 只要你不通過錯誤的函數指針類型調用函數,就可以了。 (C176.3.2.3§8) 函數指針指向函數,但它們本身就是對象,就像任何指針一樣。 因此,您可以使用void*
指向函數指針的地址 。
因此,使用void*
指向函數指針是很好的。 但不是用它直接指向一個函數。 如果是void *ptr1 = array;
數組衰減成指向第一個元素的指針,一個void (**)(void)
(相當於你的例子中的voidfunc*
)。 你可以指向一個帶有void*
函數指針這樣的指針。
此外,關於指針運算:
void*
執行指針運算。 (C17 6.3.2.2)這種算術是應該避免的常見非標准擴展。 而是使用指向字符類型的指針。 因此, (char*)ptr1 + sizeof(voidfunc);
也沒關系。 然后,從轉換void*
到voidfunc*
,以voidfunc
這是存儲陣列中的原始函數指針類型。
正如在注釋中所指出的,通過對函數類型使用typedef
,可以顯着提高此代碼的可讀性:
typedef void (voidfunc)(void);
voidfunc* array[] = {&foo, &bar}; // Step 1
void* ptr1 = array; // Step 2
void* ptr2 = (char*)ptr1 + sizeof(voidfunc*); // Step 3
voidfunc* bar_ptr = *(voidfunc**)ptr2; // Step 4
void*
上的指針算法不在C語言中。 你沒有這樣做,你在char*
上做指針運算,這是完全可以的。 您可以使用char*
而不是void*
來開始。
Andrew Helne似乎錯過了一個指向函數的指針是一個對象的事實,它的類型是一個對象類型。 這是一個簡單明了的事實,不像其他一些評論家似乎暗示的那樣掩蓋在一片神秘的東西中。 因此,他反對投射指向函數指針的指針是沒有根據的,因為指向任何對象類型的指針都可以轉換為void*
。
但是,C標准似乎不允許使用(T*)((char*)p + sizeof(T))
代替(p+1)
(其中p
是指向類型數組的元素的指針) T
),或者至少我在文中找不到這樣的許可。 因此,您的代碼可能不合法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.