我有一些代码根据参数的类型使用 _Generic 来调度函数。我不明白 gcc 生成的警告。
\n编译用gcc main.c
#include <stdio.h>\n\n#define FOOBAR(x) _Generic((x), \\\n int *: foo(x), \\\n long *: bar(x) \\\n)\n\nvoid foo(int* x) {\n printf("foo\\n");\n}\n\nvoid bar(long* y) {\n printf("bar\\n");\n}\n\nint main() {\n int a = 1111;\n long b = 2222;\n\n FOOBAR(&a);\n FOOBAR(&b);\n}\nRun Code Online (Sandbox Code Playgroud)\n现在,这段代码确实可以编译,并且 _Generic 按预期工作,即“foo”出现,然后“bar”出现。然而,编译器(gcc 和 clang)生成一个奇怪的警告,看起来它会将 _Generic 的参数匹配到错误的行:
\nmain.c: In function \xe2\x80\x98main\xe2\x80\x99:\nmain.c:20:12: warning: passing argument 1 of \xe2\x80\x98bar\xe2\x80\x99 from incompatible pointer type [-Wincompatible-pointer-types]\n 20 | FOOBAR(&a);\n | ^~\n | |\n | int *\nmain.c:5:15: note: in definition of macro \xe2\x80\x98FOOBAR\xe2\x80\x99\n 5 | long *: bar(x) \\\n | ^\nmain.c:12:16: note: expected \xe2\x80\x98long int *\xe2\x80\x99 but argument is of type \xe2\x80\x98int *\xe2\x80\x99\n 12 | void bar(long* y) {\n | ~~~~~~^\nmain.c:21:12: warning: passing argument 1 of \xe2\x80\x98foo\xe2\x80\x99 from incompatible pointer type [-Wincompatible-pointer-types]\n 21 | FOOBAR(&b);\n | ^~\n | |\n | long int *\nmain.c:4:14: note: in definition of macro \xe2\x80\x98FOOBAR\xe2\x80\x99\n 4 | int *: foo(x), \\\n | ^\nmain.c:8:15: note: expected \xe2\x80\x98int *\xe2\x80\x99 but argument is of type \xe2\x80\x98long int *\xe2\x80\x99\n 8 | void foo(int* x) {\n | ~~~~~^\nRun Code Online (Sandbox Code Playgroud)\n生成两个警告,每个 FOOBAR 一个。它似乎正在将&aint * 传递给需要 long * 的 bar,反之亦然&b。\n(注释掉 FOOBAR 之一以查看一个不兼容的指针错误。)
为什么 gcc 警告我 _Generic 正在将其参数分派给错误的函数?
\n我知道这通常不是人们使用 _Generic 的方式,即参数列表通常位于_Generic()之外。但我有一些用于分派到采用不同数量参数的函数的用例。
\n请注意,标准\xc2\xa76.5.1.1 通用选择中的示例是:
\n\n\n5 示例 cbrt 类型通用宏可以按如下方式实现:
\nRun Code Online (Sandbox Code Playgroud)\n#define cbrt(X) _Generic((X), \\\n long double: cbrtl, \\\n default: cbrt, \\\n float: cbrtf \\\n )(X)\n
请注意,函数调用的括号位于_Generic(\xe2\x80\xa6)通用选择部分之外的 \xe2\x80\x94 中。
将其调整为您的代码:
\n#include <stdio.h>\n\n#define FOOBAR(x) _Generic((x), \\\n int *: foo, \\\n long *: bar \\\n )(x)\n\nstatic void foo(int *x)\n{\n printf("foo (%d)\\n", *x);\n}\n\nstatic void bar(long *y)\n{\n printf("bar (%ld)\\n", *y);\n}\n\nint main(void)\n{\n int a = 1111;\n long b = 2222;\n\n FOOBAR(&a);\n FOOBAR(&b);\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n使用 GCC 10.2.0 set fussy 可以干净地编译(源文件gs31.c):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common -c gs31.c\n$\nRun Code Online (Sandbox Code Playgroud)\n宏外部的代码更改FOOBAR避免了我的标准编译选项请求的编译警告。
代码的 C 预处理器的输出是:
\nint main() {\n int a = 1111;\n long b = 2222;\n\n _Generic((&a), int *: foo(&a), long *: bar(&a) );\n _Generic((&b), int *: foo(&b), long *: bar(&b) );\n}\nRun Code Online (Sandbox Code Playgroud)\n和....相比:
\nint main(void)\n{\n int a = 1111;\n long b = 2222;\n\n _Generic((&a), int *: foo, long *: bar )(&a);\n _Generic((&b), int *: foo, long *: bar )(&b);\n}\nRun Code Online (Sandbox Code Playgroud)\n区别在于您的代码foo()使用 a long *(又名&b)和(又名bar())进行调用,这就是(正确地)触发警告的原因。int *&a