簡體   English   中英

你可以將“指向函數指針的指針”轉換為void *

[英]Can you cast a “pointer to a function pointer” to void*

通過評論我的回答啟發這里

這一系列步驟在C標准(C11)中是否合法?

  1. 創建一個函數指針數組
  2. 獲取指向第一個條目的指針並將該指針轉換為函數指針 void*
  3. 對該void*執行指針算術void*
  4. 將其強制轉換為指向函數指針的指針並取消引用它。

或者相當於代碼:

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)接近指針運算的另一種方法是首先將指針轉換為字符指針:在此方案中,首先將轉換后的指針中添加或減去的整數表達式乘以最初指向的對象的大小。 ,並將結果指針轉換回原始類型。 對於指針減法,字符指針之間差異的結果類似地除以最初指向的對象的大小。 當以這種方式查看時,實現僅需要在對象結束之后提供一個額外字節(其可以與程序中的另一個對象重疊)以滿足“超過最后一個元素”的要求。

是的,代碼很好。 這里有各種陷阱和轉換規則:

  • C將所有類型拆分為兩個主要類別:對象和函數。 指向函數的指針是標量類型 ,而標量類型又是一個對象。 (C17 6.2.5)
  • 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)這種算術是應該避免的常見非標准擴展。 而是使用指向字符類型的指針。
  • 作為一種特殊情況,指向字符類型的指針可用於迭代任何對象(C176.2.3.3§7)。 除了關於對齊的問題之外,這樣做是明確定義的,並且不違反“嚴格的指針別名”,如果你取消引用字符指針(C176.5§7)。

因此, (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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM