我創建了 2 個函式來讀取和寫入路徑,宣告如下:
int Read(const char * /*Filename*/, void * /*Ptr*/, size_t /*Size*/), Write(const char * /*Filename*/, const void * /*Ptr*/, size_t /*Size*/);
我創建了一個附加函式,它將使用路徑呼叫上述函式之一
static int IOData(int(*const Func)(const char *, void *, size_t)) {
char Filename[DATA_PATH_LEN];
// Build path
return Func(Filename, &Data, sizeof(Data));
}
但是,當Write作為回呼傳遞給 時IOData,編譯器會引發以下警告
不兼容的指標型別將“int (const char *, const void , int)”傳遞給“int ( )(const char *, void *, int)”型別的引數
將接受 const 指標的函式轉換為接受非 const 指標的函式是未定義的行為嗎?
我注意到有一個幾乎相同的問題,但該問題使用 C ,但該問題使用純 C,因此不能選擇使用模板
uj5u.com熱心網友回復:
這是不允許的,因為相應引數之一的型別不兼容。
兼容型別在C 標準的第 6.2.7p1 節中定義:
如果它們的型別相同,則兩種型別具有兼容型別。確定兩種型別是否兼容的附加規則在型別說明符的 6.7.2、型別限定符的 6.7.3 和宣告符的 6.7.6 中描述。...
6.7.3p10 節詳細說明了限定型別的兼容性:
對于兼容的兩個限定型別,兩者都應具有兼容型別的相同限定版本;說明符或限定符串列中型別限定符的順序不影響指定的型別。
這意味著const void *和void *不兼容。
函式型別的兼容性在 6.7.6.3p15 節中描述:
對于要兼容的兩個函式型別,都應指定兼容的回傳型別。 此外,引數型別串列(如果兩者都存在)應在引數數量和省略號終止符的使用方面達成一致;相應的引數應具有兼容的型別。如果一種型別具有引數型別串列,而另一種型別由不屬于函式定義的一部分且包含空識別符號串列的函式宣告符指定,則引數串列不應有省略號終止符,并且每個引數的型別應與應用默認引數提升所產生的型別兼容。如果一種型別具有引數型別串列,而另一種型別由包含(可能為空)識別符號串列的函式定義指定,則兩者應在引數數量上一致,并且每個原型引數的型別應與型別兼容這是由于將默認引數提升應用于相應識別符號的型別而產生的。(在確定型別兼容性和復合型別時,
所以因為一組對應的引數不兼容,所以函式型別不兼容。
最后,關于函式呼叫運算子的第 6.5.2.2p9 節()描述了在這種情況下會發生什么:
如果函式定義的型別與表示被呼叫函式的運算式所指向的(運算式的)型別不兼容,則行為未定義。
因此,通過不兼容的函式指標型別呼叫函式會觸發未定義的行為,因此不應這樣做。
uj5u.com熱心網友回復:
該標準試圖將其行為在某些合理實作上定義可能不切實際的任何行為歸類為未定義行為。因為將一個動作歸類為 UB 絕不會削弱質量實作的能力,作為“符合語言擴展”,當一個動作存在時以有用的普通方式處理該動作,所以沒有必要避免將其特征化為 UB 動作大多數實作將以相同的有用方式進行處理。
嘗試靜態確定最大堆疊使用量的實作可能合理地假設對具有特定簽名的函式指標的呼叫將僅呼叫其地址被采用且其簽名完全匹配的函式。如果標準要求指向這些函式的指標是可互換的,那可能會不可挽回地破壞靜態分析工具以前能夠容納的程式。
沒有理由期望質量實作不應該是可配置的,以便在這樣做有用和實用的情況下將此類函式指標視為可互換的,但標準放棄了對實用性和實用性的實作質量問題的管轄權。不幸的是,很難知道應該依賴哪些實作來支持這樣的結構,因為許多沒有理由不支持這樣的結構的實作并不認為它們支持它們的事實足夠值得注意以證明明確的檔案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/331925.html
上一篇:Ghidra是否誤解了函式呼叫?
下一篇:C大數乘法
