【发布时间】:2017-06-17 10:47:23
【问题描述】:
在调试我的程序时,我偶然发现了 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允许编译上述形式的表达式呢?这显然是未定义的行为。那为什么即使启用了所有警告,也没有关于此的警告?
【问题讨论】:
-
它基本上归结为通常未初始化的局部变量。强制转换本身没有区别,只是您取消引用了一个未初始化的指针,其值为 indeterminate 并因此具有 未定义的行为。
-
至于为什么GCC允许“初始化”是因为它在语法上是有效的。我 am 很惊讶它没有警告它。您使用的是什么版本的 GCC?
-
我几个月前写的一个类似问题的答案,关于为什么 gcc 会这样:stackoverflow.com/questions/42365507/…
-
@tofro 这不是编译器错误。这是通过设计完成的(我在写完链接到的答案几周后发现了这一点)。有一个警告,但默认情况下它是禁用的。
-
@pmg: "如果你省略了错误的转换,编译器会抱怨。" 你确定吗? GCC 会抱怨到底是什么,因为做了
int* a = a;?