How do I quiet the C compiler about a function poi

2019-02-14 06:21发布

问题:

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.

回答1:

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.


回答2:

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



回答3:

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


回答4:

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;


回答5:

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.