基本数据对齐问题

发布于 2024-10-09 09:23:47 字数 1485 浏览 6 评论 0原文

我一直在尝试了解我的计算机在幕后是如何工作的。我感兴趣的是看到函数内部堆栈上发生的情况。为此,我编写了以下玩具程序:

#include <stdio.h>

void __cdecl Test1(char a, unsigned long long b, char c)
{
    char c1;
    unsigned long long b1;
    char a1;
    c1 = 'b';
    b1 = 4;
    a1 = 'r';
    printf("%d %d - %d - %d %d Total: %d\n", 
        (long)&b1 - (long)&a1, (long)&c1 - (long)&b1,
        (long)&a - (long)&c1,
        (long)&b - (long)&a, (long)&c - (long)&b,
        (long)&c - (long)&a1
        );
};

struct TestStruct
{
    char a;
    unsigned long long b;
    char c;
};

void __cdecl Test2(char a, unsigned long long b, char c)
{
    TestStruct locals;
    locals.a = 'b';
    locals.b = 4;
    locals.c = 'r';
    printf("%d %d - %d - %d %d Total: %d\n", 
        (long)&locals.b - (long)&locals.a, (long)&locals.c - (long)&locals.b,
        (long)&a - (long)&locals.c,
        (long)&b - (long)&a, (long)&c - (long)&b,
        (long)&c - (long)&locals.a
        );
};

int main()
{
    Test1('f', 0, 'o');
    Test2('f', 0, 'o');
    return 0;
}

这会输出以下内容:

9 19 - 13 - 4 8 Total: 53

8 8 - 24 - 4 8 Total: 52

函数参数表现良好,但与调用约定不同已指定,我希望如此。但局部变量有点不稳定。我的问题是,为什么这些不一样?第二次调用似乎产生了更紧凑且对齐更好的堆栈。

查看 ASM 并没有什么启发(至少对我来说),因为变量地址仍然是别名。所以我想这实际上是一个关于汇编器本身将堆栈分配给局部变量的问题。

我意识到任何具体答案都可能是特定于平台的。我对一般性解释更感兴趣,除非这个怪癖确实是特定于平台的。不过,为了记录,我在 64 位 Intel 机器上使用 VS2010 进行编译。

I've been playing around to see how my computer works under the hood. What I'm interested in is seeing is what happens on the stack inside a function. To do this I've written the following toy program:

#include <stdio.h>

void __cdecl Test1(char a, unsigned long long b, char c)
{
    char c1;
    unsigned long long b1;
    char a1;
    c1 = 'b';
    b1 = 4;
    a1 = 'r';
    printf("%d %d - %d - %d %d Total: %d\n", 
        (long)&b1 - (long)&a1, (long)&c1 - (long)&b1,
        (long)&a - (long)&c1,
        (long)&b - (long)&a, (long)&c - (long)&b,
        (long)&c - (long)&a1
        );
};

struct TestStruct
{
    char a;
    unsigned long long b;
    char c;
};

void __cdecl Test2(char a, unsigned long long b, char c)
{
    TestStruct locals;
    locals.a = 'b';
    locals.b = 4;
    locals.c = 'r';
    printf("%d %d - %d - %d %d Total: %d\n", 
        (long)&locals.b - (long)&locals.a, (long)&locals.c - (long)&locals.b,
        (long)&a - (long)&locals.c,
        (long)&b - (long)&a, (long)&c - (long)&b,
        (long)&c - (long)&locals.a
        );
};

int main()
{
    Test1('f', 0, 'o');
    Test2('f', 0, 'o');
    return 0;
}

And this spits out the following:

9 19 - 13 - 4 8 Total: 53

8 8 - 24 - 4 8 Total: 52

The function args are well behaved but as the calling convention is specified, I'd expect this. But the local variables are a bit wonky. My question is, why wouldn't these be the same? The second call seems to produce a more compact and better aligned stack.

Looking at the ASM is unenlightening (at least to me), as the variable addresses are still aliased there. So I guess this is really a question about the assembler itself allocates the stack to local variables.

I realise that any specific answer is likely to be platform specific. I'm more interested in a general explanation unless this quirk really is platform specific. For the record though, I'm compiling with VS2010 on a 64bit Intel machine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白鸥掠海 2024-10-16 09:23:47

POD 结构的内存布局几乎是由平台上的语言规则 + 类型对齐/大小要求指定和保证的。

实现可以自由地处理局部变量和函数参数。它很可能只是因为您使用一元 & 将其中一些放入堆栈中。操作员获取他们的地址。

当不使用局部变量时,编译器可能会优化其初始化。当局部简单变量被非常频繁地使用时,编译器可能会使用寄存器。当局部变量仅使用一次时,编译器可以直接使用它的值来代替使用。

如果您希望更好地指定/保证函数参数布局,那么您必须使用 extern“C”链接。

Memory layout of POD structs is pretty much specified and guaranteed by language rules + type alignment/size requirements on your platform.

Implementation has free hands with the local variables and function parameters. It is most likely that it puts some of these into stack only because you use unary & operator to take their addresses.

When local variable is not used then compiler may optimize out its initialization. When local simple variable is used very intensively then compiler may use register for it. When local variable is used only once then compiler may use its value directly in place of usage.

If you want function parameters layout to be better specified/guaranteed then you have to use extern "C" linkage.

烟雨扶苏 2024-10-16 09:23:47

在 Test1 中,您定义了一堆局部变量。编译器不会被迫将它们打包在一起,或者按照您声明它们的顺序打包。

在 Test2 中,您定义一个结构体,编译器使用 http://en.wikipedia.org 填充数据/wiki/Data_struct_alignment#Typical_alignment_of_C_structs_on_x86

In Test1, you define a bunch of local variables. The compiler is not forced to pack them together, or in the same order that you declare them.

In Test2, you define an struct, and the compiler pad data using http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文