传入_Generic宏的不兼容的指针类型

以下代码生成2个警告,这些警告在问题标题中描述。

#include  static void _print_f(float *f){printf("float : %f\n", *f);} static void _print_i(int *i) {printf("int : %d\n", *i);} #define print(num) _Generic((num), \ int* : _print_i(num), \ float* : _print_f(num)) int main(void) { print((&(int){10})); print((&(float){10.f})); return 0; } 

OUTPUT:

 int : 10 float : 10.000000 

我知道,这个宏可以写成如下:

 #define print(num) _Generic((num), \ int* : _print_i, \ float* : _print_f)(num) 

在这种情况下,不会有任何警告,但我的例子是我写的一个虚拟片段来certificate问题。 在我的真实代码库中,我选择了以前的解决方案,因为其他一些“默认”但类型特定的参数需要传递给选定的函数。

所以问题是: 即使宏正在按预期工作,输出正是我所期望的,为什么会产生警告?


旗帜与环境:

 /* Mac OS X 10.9.4 Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn) */ cc -Wall -v -g -std=c11 -fmacro-backtrace-limit=0 -I/usr/local/include -c -o build/tmp/main.o main.c 

UPDATE1:

我忘了粘贴完整的回溯! 这是第一个:

 main.c:39:11: warning: incompatible pointer types passing 'int *' to parameter of type 'float *' [-Wincompatible-pointer-types] print((&(int){10})); ^~~~~~~~~~~~ main.c:31:23: note: expanded from macro 'print' float* : _print_f(num)) ^ main.c:26:29: note: passing argument to parameter 'f' here static void _print_f(float *f){printf("float : %f\n", *f);} ^ 

这是第二个:

 main.c:40:11: warning: incompatible pointer types passing 'float *' to parameter of type 'int *' [-Wincompatible-pointer-types] print((&(float){10.f})); ^~~~~~~~~~~~~~~~ main.c:30:23: note: expanded from macro 'print' int* : _print_i(num), \ ^ main.c:27:27: note: passing argument to parameter 'i' here static void _print_i(int *i) {printf("int : %d\n", *i);} ^ 

UPDATE2:

直到clang的开发人员修复了这个bug,这里有一个丑陋的解决方法来静音警告,如果assoc-list中的所有键都是类型,或者都是指向类型的指针,这将起作用; 如果类型和指向类型的指针也在键中,则会失败:

 /* HACK: re-casting pointers to mute warnings */ #define print(num) _Generic((num), \ int* : _print_i((int*)num), \ float* : _print_f((float*)num)) 

这不是clang中的错误,但不幸的是C11标准需要什么。 _Generic主表达式的所有分支必须是有效表达式,因此在所有情况下都有效。 只评估其中一个分支的事实与此无关。

您的替代版本是C11预见的情况,因为:类型generics表达式选择函数(而不是评估的调用),并将该函数应用于参数。

更正:这不是(据我所知)clang中的错误,而是对_Generic应该如何表现的正确解释。 只评估generics选择表达式中的一个generics关联,但它们都必须是有效的表达式。 ( _Generic不像宏一样。)

见Jens Gustedt的回答 。


这肯定看起来像铿锵声中的一个错误。 我有理由相信你的代码是有效的C11。 Linux Mint上的版本3.4我看到了同样的事情。

我整理了一个略微简化的演示 :

 #include  static void print_i(int i) { puts("int"); } static void print_ip(int *i) { puts("int*"); } #define print(num) _Generic((num), \ int : print_i(num), \ int* : print_ip(num)) int main(void) { int i = 10; print(i); print(&i); } 

输出正确,但我收到以下警告:

 cc:12:11: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'int *'; take the address with & [-Wint-conversion] print(i); ^ & cc:8:23: note: expanded from macro 'print' int* : print_ip(num)) ^ cc:4:27: note: passing argument to parameter 'i' here static void print_ip(int *i) { puts("int*"); } ^ cc:13:11: warning: incompatible pointer to integer conversion passing 'int *' to parameter of type 'int'; remove & [-Wint-conversion] print(&i); ^~ cc:7:22: note: expanded from macro 'print' int : print_i(num), \ ^ cc:3:25: note: passing argument to parameter 'i' here static void print_i(int i) { puts("int"); } ^ 2 warnings generated.