Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why have a "warning" when initializing an array of function pointers?

i try to initializing an array of function pointers and i have "warning":

ring-buffer.c:57:19: warning: assignment from incompatible pointer type [enabled by default]
RBufP->rbfunc[0] = &RBufPush;
                 ^

but the neighborhood's ok:

/*typedef for func pointer*/
typedef RBRetCod_t (*RBFunc)();

/*RBufP*/
typedef struct {
    RBufSiz_t size; /*size and mask*/
    RBufDat_t rbufdat;
    RBufPoint_t head, tail;
    RBFunc rbfunc[3]; /*announce of function pointers array*/
} RBuf_t;
RBuf_t *RBufP;
...

/*init for func pointers array*/
RBufP->rbfunc[2] = &RBufDel; /*it is ok*/
RBufP->rbfunc[1] = &RBufPull; /*it is ok*/
RBufP->rbfunc[0] = &RBufPush; /*it is bad, why???*/

...

/*body of the functions*/
RBRetCod_t RBufPull(unsigned char *dat)
{
        return RBSUCC;
}
RBRetCod_t RBufDel(void)
{
        return RBSUCC;
}
RBRetCod_t RBufPush(unsigned char dat)
{
        return RBSUCC;
}

please explain to me why the warning occurs in this line: RBufP->rbfunc[0] = &RBufPush;, but in adjacent rows are not there?

like image 900
Ivan Ivanovich Avatar asked May 08 '15 10:05

Ivan Ivanovich


2 Answers

See C11 section 6.7.6.3 §14, specifying when 2 function types shall be considered compatible:

[...] If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. [...]

This is the case for RBufPull and RBufDel, but not RBufPush as unsigned char gets promoted to int.

If you called RBuPush through a pointer of type RBFunc, an int argument would get pushed to the stack, whereas RBufPush would expect an unsigned char. Depending on calling convention and endianness, you'll get incorrect results.

One solution is to change RBufPush to take an int argument. Another one is to use a cast, ie

RBufP->rbfunc[0] = (RBFunc)&RBufPush;

You'll need to cast back to the correct type RBRetCod_t (*)(unsigned char) before calling rbfunc[0].

like image 94
Christoph Avatar answered Oct 29 '22 16:10

Christoph


From ISO/IEC:9899:

J.5.7 Function pointer casts

1 A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

2 A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

But this rule is limited through:

6.3.2.3 Pointers

[...]

8 A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

So you are trying to acces different functions with the same Pointerobject type. This is undefined behavior and the warning therefor is correct.

Note:

What it seems to me, what you try to achieve would be something like an interface in C#.

But this kind of functionallity is not available in C

like image 35
dhein Avatar answered Oct 29 '22 15:10

dhein