为什么允许这个指针变量赋值?

时间:2017-06-17 10:47:24

标签: c pointers gcc

在调试我的程序时,我偶然发现了gcc编译器的奇怪行为。我不知道描述这个的正确标题是什么,但请看下面的代码。

基本上,我有一个函数,它接收void* arg作为参数。然后它将它转换为另一种类型的指针HTTPRequest*。但是,我输入了错误的变量,所以代码看起来像

void someCallback(void* arg) {
   HTTPRequest* req = (HTTPRequest*) req;
   FreeRequest(req);
   //re-setup request, other stuff.. 
}

当程序尝试解除指针时,程序随FreeRequestSIGSEGV内崩溃。进一步检查后,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允许编译上述表单的表达式?这显然是未定义的行为。那么为什么没有关于此的警告,即使启用了所有警告?

1 个答案:

答案 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 *提升为正确的指针类型。