带有 _Generic 的奇怪 gcc 警告

Don*_*ald 3 c macros

我有一些代码根据参数的类型使用 _Generic 来调度函数。我不明白 gcc 生成的警告。

\n

编译用gcc main.c

\n
#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}\n
Run Code Online (Sandbox Code Playgroud)\n

现在,这段代码确实可以编译,并且 _Generic 按预期工作,即“foo”出现,然后“bar”出现。然而,编译器(gcc 和 clang)生成一个奇怪的警告,看起来它会将 _Generic 的参数匹配到错误的行:

\n
main.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      |          ~~~~~^\n
Run Code Online (Sandbox Code Playgroud)\n

生成两个警告,每个 FOOBAR 一个。它似乎正在将&aint * 传递给需要 long * 的 bar,反之亦然&b。\n(注释掉 FOOBAR 之一以查看一个不兼容的指针错误。)

\n

为什么 gcc 警告我 _Generic 正在将其参数分派给错误的函数?

\n
\n

我知道这通常不是人们使用 _Generic 的方式,即参数列表通常位于_Generic()之外。但我有一些用于分派到采用不同数量参数的函数的用例。

\n

Jon*_*ler 7

请注意,标准\xc2\xa76.5.1.1 通用选择中的示例是:

\n
\n

5 示例 cbrt 类型通用宏可以按如下方式实现:

\n
 #define cbrt(X) _Generic((X),                             \\\n                         long double: cbrtl,               \\\n                         default: cbrt,                    \\\n                         float: cbrtf                      \\\n                         )(X)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

请注意,函数调用的括号位于_Generic(\xe2\x80\xa6)通用选择部分之外的 \xe2\x80\x94 中。

\n

将其调整为您的代码:

\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}\n
Run Code Online (Sandbox Code Playgroud)\n

使用 GCC 10.2.0 set fussy 可以干净地编译(源文件gs31.c):

\n
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common -c gs31.c\n$\n
Run Code Online (Sandbox Code Playgroud)\n

宏外部的代码更改FOOBAR避免了我的标准编译选项请求的编译警告。

\n

代码的 C 预处理器的输出是:

\n
int 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}\n
Run Code Online (Sandbox Code Playgroud)\n

和....相比:

\n
int 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}\n
Run Code Online (Sandbox Code Playgroud)\n

区别在于您的代码foo()使用 a long *(又名&b)和(又名bar())进行调用,这就是(正确地)触发警告的原因。int *&a

\n