如何让 C 编译器安静下来,了解函数指针带有任意数量的参数?

发布于 2024-11-26 04:09:43 字数 733 浏览 1 评论 0原文

我的 struct 中有一个函数指针,它在运行时动态设置为代码中各个位置的另一个函数的地址。它在我的头文件中定义如下:

    void *(*run)();

在编译时,我收到以下警告:

    warning: function declaration isn't a prototype

此警告是良性的,因为指针在我的代码中的许多地方使用来调用它指向的函数,并且一切正常美好的。但是,我真的很想消除警告。

如果我将其更改为:

    void *(*run)(void);

无论我使用它,我都会遇到编译错误,因为使用指针的各种函数具有不同数量的参数,并且在括号内说 void 告诉编译器它接受没有争论。

我不能使用 va_list 或类似的任何东西,因为这只是一个指向另一个函数的指针,并且我对它们都使用一个指针,因为它使代码保持干净和简单。

我可以通过将其添加到编译器标志中来消除警告:

    -Wno-strict-prototypes

但如果可以避免的话,我宁愿不必禁用带有标志的编译器警告。

所以我的问题是:如何在代码中注释此函数指针,以使编译器满意它接受任意数量的任何类型的参数这一事实?

代码运行良好。我只是希望警告消失。

I have a function pointer inside a struct that gets dynamically set at runtime to the address of another function in various places in my code. It is defined in my header file like this:

    void *(*run)();

During compile time, I get the following warning about this:

    warning: function declaration isn't a prototype

This warning is benign, because the pointer is used in many places in my code to call the function it points to, and everything works just fine. However, I would really like to silence the warning.

If I change it to this:

    void *(*run)(void);

I get compile errors whever I use it, because the various functions that make use of the pointer have different numbers of arguments, and saying void inside the parenthesies tells the compiler it accepts no arguments.

I can't use a va_list or anything fancy like that, as this is simply a pointer to another function, and I use a single pointer for them all because it keeps the code clean and simple.

I can silence the warning with adding this to my compiler flags:

    -Wno-strict-prototypes

But I'd rather not have to disable compiler warnings with flags if I can avoid it.

So my question is: How do I notate this function pointer in the code in such a way that the compiler is satisfied with the fact that it accepts any number of any kind of arguments?

The code works perfectly. I just want the warning to go away.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

再见回来 2024-12-03 04:09:43

将指针存储为 void * 并在必要时转换为适当的函数指针类型?请记住,像调用另一种类型的函数指针一样调用一种类型的函数指针不一定是安全的,因此您开始的警告并非完全无效。

您可以像这样转换函数指针:

void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);

请注意:

  • 这里不需要显式转换,因为 void * 始终可以转换为任何指针类型。
  • (*fp) 前面的第一个“void”是函数指针的返回类型。

Store the pointer as a void * and cast to the appropriate function pointer type when necessary? Keep in mind that it isn't necessarily safe to call one type of function pointer as if it were another type, so the warning you're starting out with isn't entirely invalid.

You can cast a function pointer like so:

void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);

Note that:

  • There's no explicit casting necessary here, as void * can always be cast to any pointer type.
  • The initial "void" before (*fp) is the return type of the function pointer.
古镇旧梦 2024-12-03 04:09:43

您试图做干净的事情 - 即让编译器参与检查,但您发明的设计根本无法按照其原理做到干净。您不能以这种方式让编译器参与原型检查,因为您始终必须知道在运行时的这种特定情况下要传递哪些参数。编译器无法检查这一点,如果您犯了错误,就会出现分段错误。

但如果我没记错的话,Linux 内核中也可能使用过类似的东西(?)。解决方案是拥有一个通用指针(就像您拥有的那样),每次调用特定函数时,只需将其类型转换为具有特定参数的函数指针。您可能需要首先将其类型转换为 void * 以使编译器再次静音:-)

You are trying to do things clean - i.e. involve the compiler in checks, but the design you invented simply cannot be clean by its principle. You cannot involve compiler in prototype checks this way, because you always must know, which parameters to pass at this particular case in runtime. Compiler cannot check this and if you make a mistake, segmentation fault is on the way.

But if I remember well, something like this was maybe used also in linux kernel (?). The solution is to have a general pointer (like the one you have) and each time you call a particular function you just typecast it to the pointer to function with the particular arguments. You may need to typecast it to void * first to silence the compiler again :-)

另类 2024-12-03 04:09:43

在 C 中,当您调用没有可见原型的函数时,默认参数提升将应用于您传递给该函数的所有参数。这意味着您实际传递的类型不一定与函数接收的类型匹配。

例如,

void (*g)();
void f()
{
    float x = 0.5;
    g(x); // double passed
}

这意味着您需要知道您实际调用的函数具有与升级后传递的参数所暗示的兼容签名。

鉴于您在任何情况下都需要知道这一点,您必须知道在使用函数指针的调用站点调用的实际函数的函数签名。有了这些知识,使用具有正确原型的函数指针通常会更简单、更清晰,并且可以完全避免默认参数升级。

请注意,当您使用原型定义函数时,当您将指向函数的指针分配给没有原型的函数指针时,您实际上将 void(*)(int, int) 转换为a void(*)() 因此在调用该函数之前执行反向转换是完全正确且可取的。 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 );
    }
}

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. This means that the types that you actually pass do not necessarily match the types received by the function.

E.g.

void (*g)();
void f()
{
    float x = 0.5;
    g(x); // double passed
}

This means that you need to know that the function that you are actually calling has a compatible signature to that implied by the arguments that you are passing after promotion.

Given that you need to know this in any case you must know the function signature of the actual function being called at the call site which is using the function pointer. With this knowledge it is usually simpler and cleaner to use a function pointer with the correct prototype and you can avoid default argument promotion entirely.

Note that as you are defining your functions with prototypes, when you assigned a pointer to your function to a function pointer without a prototype you effective converted, say, a void(*)(int, int) to a void(*)() so it is completely correct and desirable to perform the reverse conversion before calling the function. gcc allows both these conversions without emitting any warnings.

E.g.

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 );
    }
}
信仰 2024-12-03 04:09:43

尝试对函数指针声明进行类型定义,然后让调用者显式转换它:

typedef void *(*run)();

//when calling...
void my_foo() {}

run r = (run)my_foo;

Try typedefing the function pointer declaration and then have the caller explicityly cast it:

typedef void *(*run)();

//when calling...
void my_foo() {}

run r = (run)my_foo;
无人问我粥可暖 2024-12-03 04:09:43

如果不同的函数签名已知,请使用联合。否则,使用 void (*)(void) 类型的指针(实际上,任何函数指针类型都可以)来保存通用指针,并在设置值和调用代码时转换为正确的类型。

使用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 *来保存函数指针 - ISO C 标准未指定此类转换,并且可能不可用在某些架构上。

忽略警告并按原样使用代码实际上也是一种可能,但请记住,任何函数参数都将受到默认参数提升的影响,并且您有责任确保提升的参数与声明的参数正确匹配。

If the different function signatures are known, use a union. Otherwise, use a pointer of type void (*)(void) (actually, any function pointer type would do) to hold the generic pointer and convert to the proper type when setting the value and calling the code.

Example using a 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);

Example using explicit casts:

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);

Don't use a void * to hold function pointers - such a conversion is unspecified by the ISO C standard and may be unavailable on certain architectures.

Ignoring the warning and using your code as-is is actually also a possibility, but keep in mind that any function argument will be subject to the default argument promotions and it's your responsibility that the promoted arguments properly match the declared parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文