根据我的理解,ELF目标文件中的符号只是内存地址的名称。换句话说,给定一个符号名称,无论在C中声明它们的类型,链接器都应始终引用相同的地址。
我发现事实并非如此。请参阅以下示例:
/* a.c */
extern void *foo;
extern void *bar;
void main() {
printf("foo: %p, bar: %p\n", foo, bar)
}
/* b.c */
void foo(void) {
}
void bar(void) {
}
上述程序的输出显示foo
和bar
指向同一位置(1)。此位置也远离foo
或bar
的地址。注意(1)表明它不是转换错误
我尝试将foo
和bar
的类型修改为函数指针(例如extern void (*foo)(void)
),输出仍然相同。
当然,它可以使用正确的声明extern void foo(void)
。
我的理解中有什么错误?谢谢。 (我试图纠正我的理解,而不仅仅是让事情有效)
答案 0 :(得分:2)
您不是要打印符号foo
和bar
所引用的地址,而是打印foo
和bar
地址的值。
要打印foo
和bar
的地址,您需要执行以下操作:
extern void *foo;
extern void *bar;
void main() {
printf("foo: %p, bar: %p\n", &foo, &bar);
}
答案 1 :(得分:2)
您的代码包含未定义的行为,因为您在不同的翻译单元中将foo
和bar
声明为不同类型的标识符。这违反了ISO 9899:2011§6.2.7¶2:
引用同一对象或函数的所有声明都应具有兼容类型; 否则,行为未定义。
当您访问静态存储中的变量时,会取消引用它以获取存储在符号中的值(这是全局变量的内容)。符号本身只是静态变量的地址。要获取符号的值,您需要使用&
运算符来获取变量的地址,即变量的值。
Ĺ是你混淆的根源,这不是函数所必需的,因为&
会自动穿插在需要的位置,参见ISO 9899:2011§6.3.2.1¶4:
函数指示符是具有函数类型的表达式。除非它是
sizeof
运算符的操作数,_Alignof
运算符, 65)或一元&
运算符,类型为“函数返回类型<的函数指示符/ em>“转换为具有类型”指向函数返回类型的指针“的表达式。65)因为没有发生这种转换,
sizeof
或_Alignof
运算符的操作数仍然是函数指示符,并且违反了6.5.3.4中的约束。
我希望这能解决你的问题。
答案 2 :(得分:0)
首先:宣言应该成为
void foo(void);
void bar(void);
默认情况下,函数已经是extern。所以你在另一个文件中声明时不需要添加extern。和extern void *foo
表示您已在另一个文件中声明指针foo并在此文件中使用它。但是你没有在另一个文件中声明指针为void *foo
。
关于获取相同的地址:2个函数或变量或任何东西可以具有相同的虚拟地址,但它们将映射到不同的内存区域(不同的物理地址)。如果你从%p打印,你得到的是虚拟地址,而不是物理地址。