gcc编译器规则改变了吗?



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

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

所以我想知道发生了什么。这本书错了吗?也可能是书中使用的编译器和我的不一样。

如果书上是这么说的,那这本书就错了。

标准答案:代码是无效的,你不允许声明名字以_开头的变量(有一些例外,这里不适用)。

系统特定的答案:尽管_ZN6myname3varEmyname::var的混乱名称(在您的系统上),但您确实需要指定它具有相同的类型。extern "C" doubleint不匹配。试试extern "C" int _ZN6myname3varE;吧。如果类型错误,编译器不知道如何正确地将值传递给printf

如评论中所述,如果intdouble传递给printf的方式足够相似,则原始代码有可能按预期工作,但在您的系统上并非如此。当在没有任何其他特殊选项的情况下使用GCC编译x86-32时,原始代码会给出预期的结果。

只是为了澄清x86_64对printf的浮点数有"特殊"处理。

因为x86_64在寄存器中传递输入,甚至是浮点数。但是浮点寄存器在SSE寄存器中传递,整型在常规寄存器中传递。由于printf不能真正知道使用了多少SSE寄存器,因此它在EAX(好吧,是AL)中作为单独的值传递。除了AL所说的,printf将从堆栈中选择值。[这种传递值的奇怪方式的原因是,你可以在寄存器中有6个整数值,然后,我认为6个SSE值。但是printf可以带任意数量的整型和浮点型参数调用。[/p>

如果你将双精度值与整数值混合并以"错误的格式"打印它们,你将得到从"随机"位置选择的值。在本例中,它将获取第一个整数寄存器的值,并将其打印出来,因为它是整数格式。您的值将作为double在SSE寄存器中传递,并且不会被printf"找到"。

然而,在大多数体系结构中(尤其是x86和ARM),所有的变量参数函数都只使用堆栈传递(或者在某些罕见的情况下"所有的寄存器类型都是一个")。这意味着无论您如何混乱地混淆数据类型,如果传递"正确的数据",无论处理器认为它是什么类型,输出都将是正确的。

最新更新