【问题标题】:Why is this pointer variable assignment allowed? [duplicate]为什么允许这个指针变量赋值? [复制]
【发布时间】: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 &amp;&amp; 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;?

标签: c pointers gcc


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2013-05-08
    • 1970-01-01
    • 2016-12-15
    • 2020-01-12
    • 1970-01-01
    • 2021-10-13
    • 2021-03-10
    • 2020-11-22
    • 2023-03-09
    相关资源
    最近更新 更多