奇怪的数据对齐

时间:2015-05-13 08:32:17

标签: c++ 64-bit memory-alignment

据我所知,数据对齐是将内存中的64位/ 32位数据块用于CPU性能,我使用的是64位linux机器,我做了一些测试并得到了一些奇怪的结果(我无法解释)行为)。

以下是我使用的结构:

  class A {
    long l0,l1,l2;
  };

  class B {
    long l0,l1,l2,l3;
  };

  class C {
    long l0,l1,l2,l3,l4;
  };

测试:

int main() {
    C* newC = new C();
    B* newB = new B();
    A* newA = new A();
    int* i = new int();

    std::cout << sizeof(A) << std::endl;
    std::cout << sizeof(B) << std::endl;
    std::cout << sizeof(C) << std::endl;

    std::cout << "C : " << newC << std::endl;
    std::cout << "B : " << newB << std::endl;
    std::cout << "A : " << newA << std::endl;
    std::cout << "i : " << i << std::endl;

    delete (i);
    delete (newC);
    delete (newA);
    delete (newB);

    return 0;
}

只需将每个对象放入堆中,我在末尾添加了一个指针,以查看newA所占用的内存

结果如下:

24
32
40
C : 0x603010
B : 0x603040
A : 0x603070
i : 0x603090
3*16 bytesnewC的地址之间的

newB:C是40个字节,已经是64位的倍数,为什么这8个字节更多?

3*16 bytesnewB之间的

newA ?? B只有32个字节,我预计:A : 0x603060

2*16 bytesnewA ??

的地址之间

i

2 个答案:

答案 0 :(得分:4)

当您在堆上分配时,您无法对地址做出任何明确的陈述。

内存分配函数很有可能维护有关已分配块的内联信息,这将影响下一个块的地址,如:

+--------+-------------+--------+-------------+
| header | alloced mem | header | alloced mem | ...
+--------+-------------+--------+-------------+

此外,为了提高效率,这些功能可能会将您的记忆整理为(例如)八或十六的倍数(您仍然不允许使用它,因为您不会知道关于它)。这可能会进一步影响您为分配的内存看到的地址。

的经典案例这些影响你的效果可以看作:

#include <iostream>
#include <cstdlib>
int main (int argc, char *argv[]) {
    char *one = new char[std::atoi(argv[1])];
    char *two = new char[std::atoi(argv[1])];
    std::cout << static_cast<void*>(one) << '\n';
    std::cout << static_cast<void*>(two) << '\n';
    return 0;
}

和脚本:

#!/usr/bin/bash
for i in {01..37}; do
    echo $i $(./qq $i)
done

在我的系统上,输出:

01 0x800102e8 0x800102f8
02 0x800102e8 0x800102f8
:: (all the same address pairs in here and in gaps below)
12 0x800102e8 0x800102f8
13 0x800102e8 0x80010300
::
20 0x800102e8 0x80010300
21 0x800102e8 0x80010308
::
28 0x800102e8 0x80010308
29 0x800102e8 0x80010310
::
36 0x800102e8 0x80010310
37 0x800102e8 0x80010318
当你只分配一个字符时,

在两者之间提供高达 16个字节。

保持 16个字节一直到new char[12]的事实,并且每次在之后添加8个字符时增加8个似乎表示四个字节的头,头+数据的最小十六个字节,头+数据区的八字节分辨率。

请记住,基于我对这些事情往往被写入的方式的了解,而受过教育的猜测,仍然猜测,你不应该依赖它。它可能使用与我的想法完全不同的策略,或者它可能会改变其对更大块的策略。

如果您想知道实际所占类型的空间大小,请创建一个两个数组,并计算x[0]x[1]之间内存地址的差异。您会发现它应该与您从sizeof获得的相同。

答案 1 :(得分:2)

您不能假设后续调用new将在内存中返回连续的块。

如果您想尝试测试用例,我建议您执行以下操作:

  struct D{
    C c;
    B b;
    A a;
  }

现在您可以开始打印地址了。但是只有浮点数,我预计没有对齐问题。