C 将宏的容器_应用于嵌入式字符数组时报告警告
当我将宏的C 将宏的容器_应用于嵌入式字符数组时报告警告,c,gcc,macros,gcc-warning,C,Gcc,Macros,Gcc Warning,当我将宏的container\u应用于包含字符数组的C结构时,我得到了警告:从不兼容的指针类型初始化 代码如下: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct st {
container\u应用于包含字符数组的C结构时,我得到了警告:从不兼容的指针类型初始化
代码如下:
#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;
}
似乎\uu mptr
的类型是[]
,这是由typeof()
推导出来的。但是,ptr
本身是类型(*)[]
。显然,它们是不一样的
此外,如果我用clang编译这段代码,一切都正常。GCC似乎有一个更严格的类型检查规则
问:如何更正此警告 声明\uu mptr
是无用的,因为它只是在下一行中转换为char*
。只需将宏替换为:
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
注意:GCC 6.2.0没有对原始代码给出任何警告,除非对于未使用的变量s
数组类型本身不是const
,而const
实际上是来自const
的每个成员。实际上,没有语法允许您声明数组本身是const
。您可以阅读更多详细信息
然而,GCC中的typeof
宏扩展似乎存在缺陷,因为如果typeof
解析为数组类型,则const
限定符将应用于数组,而不是其单个成员。(注意到该问题似乎已在较新版本的GCC中得到解决。)
我不知道你会认为什么是可接受的解决方案,但是下面的编译没有注意到的警告。
const struct st *ct = &t;
typeof(ct->c) *p = &ct->c;
struct st *s = container_of(p, struct st, c);
如果您知道传递给
的container\u的成员是一个数组,则可以传递该数组的一个元素,以避免出现警告:
char *p = &t.c[0]; /* or: char *p = t->c; */
struct st *s = container_of(p, struct st, c[0]);
另一种避免警告的方法是将指针设为指向void
的指针:
void *p = &t.c;
struct st *s = container_of(p, struct st, c);
关于原始代码,GCC的行为似乎在GCC 4.9和GCC 5.4.1之间有所改变。GCC的更高版本不会为原始代码生成警告“不兼容指针类型”。但是,在更高版本的GCC中启用-Wpedantic
会产生警告“指向具有不同限定符的数组的指针在ISO C[-Wpedantic]中不兼容”。您的错误消息应该包括实际类型。(*)[]
不是一种类型,[]
也不是。对于您的回答,这里的[]
代表数组类型,(*)[]
代表指向数组的指针。我很难相信clang接受此代码。至少,它依赖于一个扩展来实现这一点。宏扩展为一个带括号的代码块,而这不是一个表达式——它根本不表示值。@JohnBollinger是的,它依赖于两个GCC扩展。我相信,这个宏定义来自Linux内核(至少它目前在Linux内核中使用)。Windows NT头中的等效宏是包含_RECORD
,当然它不使用任何GCC扩展。Linux内核中
宏的容器有点缺陷,但是如果必须使用它,并且您知道成员是数组,则可以使用数组的元素而不是数组本身。即char*p=&t.c[0];struct st*s=容器(p,struct st,c[0])
如果常量
是唯一的问题,那么简单地删除它应该是一个可行的选择,因为常量
特性会立即消失。我能想到的唯一原因是处理这样的情况:宏的ptr
参数是指向引用类型的const
限定版本的指针,但即使如此,安德烈·萨西的建议似乎更合适。@JohnBollinger:我的建议仅适用于不需要更改宏定义的情况。