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