gcc编译器规则是否发生了变化?

时间:2014-03-08 14:20:57

标签: c++ gcc

我一直在读一本关于编译器和链接器的书。我在书中得到了一个片段:

#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 。它只返回一个随机数。

所以我想知道会发生什么。这本书错了吗?或者可能是本书使用的编译器与我的编译器有所不同。

提前致谢。

2 个答案:

答案 0 :(得分:2)

如果这就是这本书的内容,那么这本书就错了。

标准答案:代码无效,不允许声明名称以_开头的变量(有些例外情况不适用于此处)。

系统特定的答案:虽然_ZN6myname3varEmyname::var的受损名称(在您的系统上),但您需要指定它具有相同的类型。 extern "C" doubleint不匹配。请尝试使用extern "C" int _ZN6myname3varE;。如果输入的类型错误,编译器就不知道如何正确地将值传递给printf

正如评论中所述,如果intdouble传递给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”使用“一个寄存器类型”)。这意味着无论您混淆数据类型多么混乱,如果您传递“正确的数据”,输出将是正确的,无论处理器认为它是什么类型。