簡體   English   中英

關於 function 指針采用任意數量的 arguments,如何使 C 編譯器安靜下來?

[英]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.

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