如何正确使用C23的typeof_unqual避免const限定符丢弃编译警告
问题背景
最初实现的copyThing宏利用typeof让返回值与参数类型一致,编译无警告:
extern void *copyThingImpl(const void *x); #define copyThing(x) ((typeof(x)) copyThingImpl(x)) void foo(void); void foo(void) { char *x = "foo"; char *y; y = copyThing(x); (void) y; }
编译命令:gcc -Wall -O2 -c test.c
但当参数改为const指针时,会触发警告:
extern void *copyThingImpl(const void *x); #define copyThing(x) ((typeof(x)) copyThingImpl(x)) void foo(void); void foo(void) { const char *x = "foo"; char *y; y = copyThing(x); (void) y; }
警告信息:
warning: assignment discards 'const' qualifier from pointer target type
原因是typeof(x)的结果是const char *,赋值给非const的y会丢弃const限定符。
尝试用C23新增的typeof_unqual修改宏后,编译仍出现相同警告:
extern void *copyThingImpl(const void *x); #define copyThing(x) ((typeof_unqual(x)) copyThingImpl(x)) void foo(void); void foo(void) { const char *x = "foo"; char *y; y = copyThing(x); (void) y; }
编译命令:gcc -Wall -O2 -std=c2x -c test.c
你的typeof_unqual用法有误
问题出在对typeof_unqual作用的理解上:它只会移除类型的顶层限定符,而const char *中的const属于底层限定符(修饰的是指针指向的内容,而非指针本身),所以typeof_unqual(const char *)的结果仍然是const char *,赋值给char *y自然还是会触发警告。
举几个例子明确顶层/底层限定符的区别:
typeof_unqual(const int)→int(const是顶层限定符,被移除)typeof_unqual(int *const)→int *(const修饰指针本身,属于顶层,被移除)typeof_unqual(const int *)→const int *(const修饰指针指向的对象,属于底层,不会被移除)
typeof_unqual的正确作用
C23标准中,typeof_unqual(T)的作用是移除类型T的顶层限定符,包括const、volatile、restrict、_Atomic这些直接修饰类型本身的限定符。它不会修改底层的限定符(比如指针指向对象的const属性)。
解决问题的方法
你需要的是移除指针指向类型的const,而非指针本身的顶层限定符,可以结合typeof和typeof_unqual实现:
extern void *copyThingImpl(const void *x); #define copyThing(x) ((typeof(typeof_unqual(*x)) *) copyThingImpl(x)) void foo(void) { const char *x = "foo"; char *y; y = copyThing(x); (void) y; }
解释:
*x的类型是const char,typeof_unqual(*x)会移除这个顶层const,得到char- 再通过
typeof(...) *将其转换为char *类型,与y的类型匹配,赋值时就不会触发警告了
内容的提问来源于stack exchange,提问作者Peter Eisentraut

