[英]How do I quiet the C compiler about a function pointer takes any number of arguments?
我在一個struct
中有一個 function 指針,該指針在運行時動態設置為我代碼中不同位置的另一個 function 的地址。 它在我的 header 文件中定義如下:
void *(*run)();
在編譯期間,我收到以下警告:
warning: function declaration isn't a prototype
這個警告是良性的,因為在我的代碼中的許多地方都使用了指針來調用它指向的 function,並且一切正常。 但是,我真的很想消除警告。
如果我將其更改為:
void *(*run)(void);
無論我使用它,我都會遇到編譯錯誤,因為使用指針的各種函數具有不同數量的 arguments,並且括號內的void
告訴編譯器它不接受 arguments。
我不能使用va_list
或任何類似的東西,因為這只是指向另一個 function 的指針,我對它們都使用單個指針,因為它使代碼保持簡潔。
我可以通過將其添加到我的編譯器標志來使警告靜音:
-Wno-strict-prototypes
但是,如果可以避免的話,我寧願不必禁用帶有標志的編譯器警告。
所以我的問題是:我如何在代碼中注釋這個 function 指針,以使編譯器對它接受任意數量的任何類型的 arguments 這一事實感到滿意?
該代碼完美運行。 我只想要 go 的警告。
將指針存儲為void *
並在必要時轉換為適當的 function 指針類型? 請記住,將一種類型的 function 指針調用為另一種類型並不一定安全,因此您開始使用的警告並非完全無效。
您可以像這樣轉換 function 指針:
void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);
注意:
void *
總是可以轉換為任何指針類型。(*fp)
之前的初始“void”是 function 指針的返回類型。您正在嘗試做干凈的事情 - 即讓編譯器參與檢查,但是您發明的設計根本無法按照其原理進行清潔。 您不能以這種方式讓編譯器參與原型檢查,因為您始終必須知道在運行時的這種特殊情況下要傳遞哪些參數。 編譯器無法檢查這一點,如果你犯了錯誤,就會出現分段錯誤。
但如果我沒記錯的話,linux kernel(?)中也可能使用過類似的東西。 解決方案是擁有一個通用指針(就像您擁有的那樣),每次調用特定的 function 時,您只需將其類型轉換為指向 function 的指針,並帶有特定的 ZDBC11CAA5BDA99F77E6FB4DABD882E7。 您可能需要先將其類型轉換為 void * 以再次使編譯器靜音:-)
In C, when you call a function without a prototype visible, default argument promotions are applied to all of the arguments that you pass to the function. 這意味着您實際傳遞的類型不一定與 function 接收的類型匹配。
例如
void (*g)();
void f()
{
float x = 0.5;
g(x); // double passed
}
這意味着您需要知道您實際調用的 function 與您在升級后傳遞的 arguments 所暗示的簽名兼容。
鑒於您在任何情況下都需要知道這一點,您必須知道在使用 function 指針的調用站點上調用的實際 function 的 function 簽名。 有了這些知識,使用具有正確原型的 function 指針通常更簡單、更清晰,並且您可以完全避免默認參數提升。
請注意,當您使用原型定義函數時,當您將指向 function 的指針分配給沒有原型的 function 指針時,您可以有效地將void(*)(int, int)
轉換為void(*)()
因此在調用 function 之前執行反向轉換是完全正確和可取的。 gcc 允許這兩種轉換而不會發出任何警告。
例如
void PerformCall( void(*p)() )
{
if (some_condition)
{
// due to extra knowledge I now know p takes two int arguments
// so use a function pointer with the correct prototype.
void(*prototyped_p)(int, int) = p;
prototyped_p( 3, 4 );
}
}
嘗試 typedefing function 指針聲明,然后讓調用者顯式地轉換它:
typedef void *(*run)();
//when calling...
void my_foo() {}
run r = (run)my_foo;
如果已知不同的 function 簽名,請使用union
。 否則,使用void (*)(void)
類型的指針(實際上,任何 function 指針類型都可以)來保存通用指針並在設置值和調用代碼時轉換為正確的類型。
使用union
的示例:
union run_fn
{
void *(*as_unary)(int);
void *(*as_binary)(int, int);
};
struct foo
{
union run_fn run;
};
void *bar(int, int);
struct foo foo;
foo.run.as_binary = bar;
void *baz = foo.run.as_binary(42, -1);
使用顯式強制轉換的示例:
struct foo
{
void (*run)(void);
};
void *bar(int, int);
struct foo foo;
foo.run = (void *(*)(int, int))bar;
void *baz = ((void *(*)(int, int))foo.run)(42, -1);
不要使用void *
來保存 function 指針 - ISO C 標准未指定此類轉換,並且可能在某些架構上不可用。
忽略警告並按原樣使用代碼實際上也是一種可能性,但請記住,任何 function 參數都將受到默認參數提升的影響,提升的 arguments 與聲明的參數正確匹配是您的責任。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.