extern 变量原型:数组不等于空指针的比较始终为 true

问题描述 投票:0回答:2

在清理旧程序时,GCC 返回了一个我无法理解的警告。 这是一个片段:

#include <stdio.h>
#include <stdint.h>

extern int *foo[];

int main(void) {
    if (foo != NULL)
        printf("Foo is not null\n");
    return 0;
}
$ gcc -Wall main.c
main.c: In function 'main':
main.c:7:17: warning: the comparison will always evaluate as 'true' for the address of 'foo' will never be NULL [-Waddress]
    7 |         if (foo != NULL)
      |                 ^~
main.c:4:13: note: 'foo' declared here
    4 | extern int *foo[];
      |      

然后我尝试将原型更改为

extern int **foo;

$ gcc -Wall main.c
/usr/bin/ld: /tmp/cciux1Df.o: warning: relocation against `foo' in read-only section `.text'
  1. 如此处所示,在没有初始化的情况下,它们应该被认为是相同的。 为什么

    **foo
    (正确地)无法作为未定义的引用进行编译,而 GCC 可以使用
    *foo[]
    成功编译?

  2. 错误

    relocation against 'foo' in read-only section '.text'
    作为警告真正意味着什么?

谢谢。

arrays c if-statement declaration
2个回答
4
投票

编译器试图告诉你这个语句

if (foo != NULL)

没有意义,因为数组总是占用内存,而你声明了一个数组

extern int *foo[];
.

在 if 语句中,数组指示符

foo
隐式转换为
int **
类型的指针,指向数组的第一个元素(即指向数组占用的内存范围)。所以它不能等于
NULL

请注意,您应该在这个或其他翻译单元中的某个位置定义数组。否则,链接器可能会发出数组未定义的错误。因为上面的数组声明不是它的定义,也不是它的暂定定义。


1
投票

此测试可能被用作弱链接的一种形式。在某些情况下,程序可能已与数组的定义链接,而在其他情况下,链接器会为符号分配零值(链接器符号的零值对应于 C 中数组的零地址) .

这种行为不会由 C 标准定义,但这些旧代码可能没有被编写为符合标准的代码,并且这可能适用于当时使用的编译器和链接器。

如此处所示,在没有初始化的情况下,它们应被视为相同。

这是数组参数上下文中的 C++ 问题。您不应该在涉及此问题时使用它。

为什么

**foo
(正确地)无法编译为未定义的引用,而 GCC 可以成功编译
*foo[]

使用

extern int *foo[];
foo
中的
foo != NULL
被转换为其第一个元素的地址,并且编译器假设它永远不会为空,因此它将
foo != NULL
优化为“true”。因此,生成的目标代码不包含对
foo
的任何引用,因为优化会删除它。

使用

extern int **foo;
时,
foo
中的
foo != NULL
需要获取
foo
中存储的值来测试它是否为空指针,因此生成的目标代码会尝试从
foo
加载数据。然后链接器无法满足对
foo
的引用,因此它会抱怨。

© www.soinside.com 2019 - 2024. All rights reserved.