在清理旧程序时,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'
如此处所示,在没有初始化的情况下,它们应该被认为是相同的。 为什么
**foo
(正确地)无法作为未定义的引用进行编译,而 GCC 可以使用 *foo[]
成功编译?
错误
relocation against 'foo' in read-only section '.text'
作为警告真正意味着什么?
谢谢。
编译器试图告诉你这个语句
if (foo != NULL)
没有意义,因为数组总是占用内存,而你声明了一个数组
extern int *foo[];
.
在 if 语句中,数组指示符
foo
隐式转换为 int **
类型的指针,指向数组的第一个元素(即指向数组占用的内存范围)。所以它不能等于NULL
。
请注意,您应该在这个或其他翻译单元中的某个位置定义数组。否则,链接器可能会发出数组未定义的错误。因为上面的数组声明不是它的定义,也不是它的暂定定义。
此测试可能被用作弱链接的一种形式。在某些情况下,程序可能已与数组的定义链接,而在其他情况下,链接器会为符号分配零值(链接器符号的零值对应于 C 中数组的零地址) .
这种行为不会由 C 标准定义,但这些旧代码可能没有被编写为符合标准的代码,并且这可能适用于当时使用的编译器和链接器。
如此处所示,在没有初始化的情况下,它们应被视为相同。
这是数组参数上下文中的 C++ 问题。您不应该在涉及此问题时使用它。
为什么
(正确地)无法编译为未定义的引用,而 GCC 可以成功编译**foo
?*foo[]
使用
extern int *foo[];
,foo
中的foo != NULL
被转换为其第一个元素的地址,并且编译器假设它永远不会为空,因此它将foo != NULL
优化为“true”。因此,生成的目标代码不包含对 foo
的任何引用,因为优化会删除它。
使用
extern int **foo;
时,foo
中的foo != NULL
需要获取foo
中存储的值来测试它是否为空指针,因此生成的目标代码会尝试从foo
加载数据。然后链接器无法满足对 foo
的引用,因此它会抱怨。