2

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?

6
  • 3
    Your error message should include the actual types. And (*)[] is not a type, nor is []. Commented Oct 10, 2016 at 15:07
  • thx for your response, here [] stands for array type, and (*)[] stands for pointer to array. Commented Oct 10, 2016 at 15:13
  • I have difficulty believing that clang accepts this code. At minimum, it is relying on an extension to do so. The macro expands to a parenthesized block of code, and that is not an expression -- it does not represent a value at all. Commented Oct 10, 2016 at 15:27
  • @JohnBollinger Yes, it's relying on two GCC extensions. This macro definition comes from the Linux kernel, I believe (at least it is currently used in the Linux kernel). The equivalent macro in the Windows NT headers is CONTAINING_RECORD, which doesn't use any GCC extensions, of course. Commented Oct 10, 2016 at 17:16
  • The container_of macro from the Linux kernel is somewhat deficient, but if you have to use it, and you know the member is an array, you can use an element of the array instead of the array itself. I.e. char *p = &t.c[0]; struct st *s = container_of(p, struct st, c[0]); Commented Oct 10, 2016 at 17:43

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

An array type is itself not const, the constness 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);

2 Comments

If the const is the only problem then simply removing it should be a viable option, since the constness is immediately cast away. The only reason I can think of for it is to handle the case where the macro's ptr argument is a pointer to the const-qualified version of the referenced type, but even then, André Sassi's suggestion seems more appropriate.
@JohnBollinger: My suggestion only applies if changing the macro definition is not an option.
1

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]".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.