简体   繁体   中英

Report warning when apply container_of macro to embedded char array

When I apply container_of macro to a C struct which contains an array of char, I got warning: initialization from incompatible pointer type .

Here is the codes:

#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})


struct st {
    int a;
    char b;
    char c[16];
    void *p;
};

int main(void)
{
    struct st t = {
        .a = 101,
        .b = 'B',
        .c = "hello",
        .p = NULL
    };

    char (*p)[16] = &t.c;
    struct st *s = container_of(p, struct st, c);

    return 0;
}

It seems that the type of __mptr is [] , which is deduced by typeof() . However, ptr itself is type (*)[] . Obviously, they are not the same.

Besides, if I compile this code by clang , everything is OK. GCC seems have a more strict rule for type checking.

Q: How to correct this warning?

The declartion of __mptr is unuseful, since it is simply cast to char* in the next line. Just replace the macro with:

#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

Note: GCC 6.2.0 didn't give any warning for the original code, unless for the unused variable s .

An array type is itself not const , the const ness is actually from each individual member being const . There is actually no syntax allowing you to declare that an array itself is const . You can read this post for additional details.

It seems, however, that the typeof macro extension in GCC is flawed in that if typeof resolves to an array type, the const qualifier applies to the array rather than to its individual members. ( André Sassi has noted this issue seems to have been resolved in a newer version of GCC.)

I don't know what you would consider an acceptable workaround, but the following compiles without the noted warning.

const struct st *ct = &t;
typeof(ct->c) *p = &ct->c;
struct st *s = container_of(p, struct st, c);

If you know the member being passed to container_of is an array, you can pass an element of that array instead to avoid the warning:

char *p = &t.c[0]; /* or: char *p = t->c; */
struct st *s = container_of(p, struct st, c[0]);

Another way to avoid the warning is to make the pointer a pointer to void :

void *p = &t.c;
struct st *s = container_of(p, struct st, c);

Regarding the original code, it seems the behaviour of GCC changed sometime between GCC 4.9 and GCC 5.4.1. The later version of GCC doesn't produce the warning "incompatible pointer type" for the original code. However, enabling -Wpedantic with the later version of GCC produces the warning "pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM