在调试我的程序时,我偶然发现了gcc编译器的奇怪行为。我不知道描述这个的正确标题是什么,但请看下面的代码。
基本上,我有一个函数,它接收void* arg
作为参数。然后它将它转换为另一种类型的指针HTTPRequest*
。但是,我输入了错误的变量,所以代码看起来像
void someCallback(void* arg) {
HTTPRequest* req = (HTTPRequest*) req;
FreeRequest(req);
//re-setup request, other stuff..
}
当程序尝试解除指针时,程序随FreeRequest
在SIGSEGV
内崩溃。进一步检查后,req
的值始终为NULL
。
我花了一段时间才意识到我所做的演员才是在错误的变奏曲上完成的 - 应该是
HTTPRequest* req = (HTTPRequest*) arg;
然后这一切都奏效了。然而,我感到困惑的是,gcc
不仅允许我编译此代码,而且在此行中不会发出任何警告。
考虑最小的例子
#include <stdlib.h>
#include <stdio.h>
void someFunc(void* arg) {
int* a = (int*) a;
printf("a = %p\n", a);
printf("*a = %d\n", *a);
}
int main() {
int b = 42;
someFunc(&b);
return 0;
}
编译
gcc -Wall -Wextra -Wpedantic -o test -O0 test.c && test.exe
编译器输出
test.c: In function 'someFunc':
test.c:7:9: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'int *' [-Wformat=]
printf("a = %p\n", a);
^
test.c:4:21: warning: unused parameter 'arg' [-Wunused-parameter]
void someFunc(void* arg) {
^
程序输出:
a = 0061FFCC
*a = 6422500
在至少O1
进行优化后,它会输出:
gcc -Wall -Wextra -pedantic -o test -O1 test.c && test.exe
test.c: In function 'someFunc':
test.c:7:9: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'int *' [-Wformat=]
printf("a = %p\n", a);
^
test.c:4:21: warning: unused parameter 'arg' [-Wunused-parameter]
void someFunc(void* arg) {
^
输出
a = 00000000
然后挂起。
所以问题是:为什么gcc允许编译上述表单的表达式?这显然是未定义的行为。那么为什么没有关于此的警告,即使启用了所有警告?
答案 0 :(得分:3)
为什么gcc允许编译上述表单的表达式?这显然是未定义的行为。
因为标准允许这个。未定义的行为允许编译器优化C中的代码写入。您可以阅读this excellent article from llvm developer。
那为什么即使启用了所有警告,也没有关于此的警告?
-Wall -Wextra
不会在gcc中激活所有警告。 clang有-Weverything
但是,gcc没有这样的选择。
编译器不会强制为所有内容打印警告消息。如果编译器想要符合标准,则几乎没有警告必须打印。
您所做的是未定义的行为,并且标准中不一定要打印警告。
clang 4.0.0警告此-Wuninitialized
:
warning: variable 'a' is uninitialized when used within its own initialization [-Wuninitialized]
int* a = (int*) a;
gcc 7.1.1警告此-Winit-self
:
warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
int* a = (int*) a;
注意:你的演员阵容没用,C会将任何void *
提升为正确的指针类型。