簡體   English   中英

這個函數指針的前向聲明在C中是否有效?

[英]Is this forward declaration of a function pointer valid in C?

我試圖找出以下前向聲明在ANSI-C中是否有效:

第一檔:

extern void * fptr;   // opaque forward declaration.
int main (void) {
  fptr = NULL;        // set the function pointer to NULL
}

第二檔:

typedef int (*fptr_t)(int);
fptr_t fptr;         // real declaration of the function pointer

對我來說,這應該是無效的,因為fptr如果用兩種不同的類型聲明,但gccclang都沒有給出任何警告。

我會更具體地對C11標准的精確點感興趣,這些點可以得出結論為什么它有效(或無效)。


編輯:在C11標准中,6.2.7:2說:

引用同一對象或函數的所有聲明都應具有兼容類型; 否則,行為未定義。

但是我找不到如何判斷void*是否與fptr_t兼容。

C99:

6.2.7兼容型和復合型

第2條:

引用同一對象或函數的所有聲明都應具有兼容類型; 否則,行為未定義。

6.7.5.1指針聲明符

第2條:

要使兩個指針類型兼容,兩者都應具有相同的限定條件,並且兩者都應是兼容類型的指針。

如果不進一步深入挖掘標准,很容易看出void和功能不兼容。

我願意打賭這不會改變C11。 C已經隱式支持不同的代碼和數據空間以及代碼和數據指針的不同大小和表示很長一段時間,刪除此功能並將語言限制在可用的較小機器子集中會很奇怪。 因此,謹慎投票。 更好的證據。

不,它無效,因為基本上你將一個常規指針( NULLvoid* )存儲到一個實際上是函數指針的內存位置。 您只是將其從編譯器中隱藏起來,並且鏈接器並不關心,但是在一天結束時您有未定義的行為,因為兩個指針類型不一定兼容。 當然它可能適用於許多系統,但也許並非全部。

有關函數指針vs void指針的更多信息,請參見: can void *用於存儲函數指針? - 雖然這與你所呈現的情況略有不同,但答案仍然相關。

它應該工作,但它是無效的。 在第一個文件中,您聲明標識符fptr將在另一個編譯單元中定義,並且它將是void * 在第二個文件中,您定義標識符,但它現在是一個指向函數的指針。 編譯后的文件通常不保留對象的類型(只有地址),所以:

  • 編譯器不知道其他源聲明了不同的類型,並且不能發出任何警告
  • 鏈接器不需要控制它,因此標准不需要警告,常見的實現不控制類型,因此也沒有警告

在通常的實現中,所有指針具有相同的表示,並且指向函數的指針到指向void的指針的轉換不會改變表示,因此別名將給出預期的結果。

但它仍然是每個標准的未定義行為 (*),因為6.2.5類型§27聲明(強調我的):

指向void的指針應具有與指向字符類型的指針相同的表示和對齊要求.39)類似地,指向兼容類型的限定或非限定版本的指針應具有相同的表示和對齊要求。 所有指向結構類型的指針都應具有相同的表示和對齊要求。 所有指向union類型的指針都應具有相同的表示和對齊要求。 指向其他類型的指針不需要具有相同的表示或對齊要求

(*)由於沒有通過常規實現進行測試,因此當前版本的編譯器將檢測和優化UB的風險很小。 但我永遠不會在生產代碼中這樣做......

void *是一個對象類型指針,它與函數類型指針不同。 它們不兼容。

但:

可行性問題J.5.7 函數指針強制轉換

  1. 指向對象或void的指針可以轉換為指向函數的指針,允許將數據作為函數調用(6.5.4)。

  2. 指向函數的指針可以轉換為指向對象的指針或void,允許檢查或修改函數(例如,通過調試器)(6.5.4)。

那么為什么不完全隱藏模塊中的指針,並外化函數來操縱它? 這樣可以避免混疊問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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