【发布时间】:2021-12-28 22:41:33
【问题描述】:
我正在学习一些有关链接的基础知识,并遇到了以下代码。
文件:f1.c
#include <stdio.h>
static int foo;
int main() {
int *bar();
printf("%ld\n", bar() - &foo);
return 0;
}
文件:f2.c
int foo = 0;
int *bar() {
return &foo;
}
然后一个问题问我这句话是否正确:无论程序如何编译,链接或运行,它的输出都必须是一个常数(相对于多次运行)并且它是非零的。
我认为这是正确的。虽然foo 有两个定义,但其中一个是用static 声明的,因此它会影响全局foo,因此链接器不会只选择一个foo。由于变量的相对位置在运行时应该是固定的(虽然绝对地址可以变化),所以输出必须是一个常数。
我对代码进行了试验,在gcc 7.5.0 和gcc f1.c f2.c -o test && ./test 上它总是会输出1(但如果我删除static,它会输出0)。但是答案说上面的说法是错误的。我想知道为什么。我的理解有什么错误吗?
上下文。 这是与计算机系统的链接章节相关的问题:Randal E. Bryant 和 David R. O'Hallaron 的程序员视角。但它不是出自书本。
更新。好的,现在我找到了原因。如果我们交换顺序并编译为gcc f2.c f1.c -o test && ./test,它将输出-1。好无聊的问题……
【问题讨论】:
-
除非您首先将指针转换为合适的整数类型,否则从技术上讲,进行减法应该是未定义的行为,但实际上(或在 uintptr_t 中进行减法时),只要文件最终编译成同一个二进制文件(如果它们最终在不同的共享库中,则相对位置可能会在多次运行之间发生变化)。
-
即使有适当的演员表,请参阅port70.net/~nsz/c/c11/n1570.html#6.5.6p9。这两个地址不满足约束,所以行为没有很好的定义。
-
@PSkocik 在这种情况下,由于您提到的原因,没关系。只要您不期望减法产生有意义的结果,就可以(总是)减去指针。使用
!=或==而不是-可能更可口。或者,只需使用%p打印 -
Re “它会影响全局
foo”:C 没有任何全局名称空间(int等关键字除外)或范围。它最大的作用域是文件作用域,两个foo在不同的文件中(翻译单元)。使名称引用同一个对象是通过链接完成的,而static int foo;具有内部链接,因此链接器在解析外部链接时看不到。 -
是否“无论程序如何编译、链接或运行,它的输出必须是一个常数(相对于多次运行)并且它是非零的。”意味着如果程序链接不同,输出可能会有所不同?例如,给定我的特定系统,
cc -o f f1.c f2.c && ./f打印“1”,而cc -o f f2.c f1.c && ./f打印“-1”。