我一直在读一本关于编译器和链接器的书。我在书中得到了一个片段:
#include <stdio.h>
namespace myname {
int var = 42;
}
extern "C" double _ZN6myname3varE;
int main()
{
printf("%dn", _ZN6myname3varE);
return 0;
}
根据书,我想我可以理解这个片段,书告诉它将返回42。书中给出的运行代码片段的代码是:
$ g++ ManaulNameMangling.cpp -o ManaulNameMangling
$ ./ManualNameMangling
42
但是当我在我的Ubuntu上做同样的事情时,我无法得到42。它只是返回一个随机数。
所以我想知道发生了什么。这本书错了吗?也可能是书中使用的编译器和我的不一样。
如果书上是这么说的,那这本书就错了。
标准答案:代码是无效的,你不允许声明名字以_
开头的变量(有一些例外,这里不适用)。
系统特定的答案:尽管_ZN6myname3varE
是myname::var
的混乱名称(在您的系统上),但您确实需要指定它具有相同的类型。extern "C" double
和int
不匹配。试试extern "C" int _ZN6myname3varE;
吧。如果类型错误,编译器不知道如何正确地将值传递给printf
。
如评论中所述,如果int
和double
传递给printf
的方式足够相似,则原始代码有可能按预期工作,但在您的系统上并非如此。当在没有任何其他特殊选项的情况下使用GCC编译x86-32时,原始代码会给出预期的结果。
只是为了澄清x86_64对printf
的浮点数有"特殊"处理。
因为x86_64在寄存器中传递输入,甚至是浮点数。但是浮点寄存器在SSE寄存器中传递,整型在常规寄存器中传递。由于printf不能真正知道使用了多少SSE寄存器,因此它在EAX(好吧,是AL)中作为单独的值传递。除了AL所说的,printf将从堆栈中选择值。[这种传递值的奇怪方式的原因是,你可以在寄存器中有6个整数值,然后,我认为6个SSE值。但是printf可以带任意数量的整型和浮点型参数调用。[/p>
如果你将双精度值与整数值混合并以"错误的格式"打印它们,你将得到从"随机"位置选择的值。在本例中,它将获取第一个整数寄存器的值,并将其打印出来,因为它是整数格式。您的值将作为double
在SSE寄存器中传递,并且不会被printf"找到"。