【问题标题】:Confuse about data address alignment混淆数据地址对齐
【发布时间】:2016-11-19 07:46:52
【问题描述】:

我对

提供的答案有疑问

@dan04。 What is aligned memory allocation?

特别是,如果我有这样的事情:

int main(){
      int num;  // 4byte
      char s;   // 1byte
      int *ptr;


}

如果我有一台 32 位的机器,你认为它仍然会默认填充数据吗?

在上一个问题中,有人问过struct,我问的是main 中声明的变量。

更新:

a = 2 bytes 
b = 4 bytes
c = 1 byte
d = 1 byte



 0 1 2 3 4 5 6 7
|a|a|b|b|b|b|c|d|  bytes
|       |       |  words

【问题讨论】:

  • 指针也需要存储在内存中。
  • 你为什么认为这些变量存储在任何地方?这些是自动变量,它们可能会被优化掉,或者只存在于寄存器中。此外,如果曾经存储在内存中,编译器可以重新排序它们,如果它认为这有用等。

标签: c++ c memory-alignment


【解决方案1】:

对此没有规定。这取决于您使用的实现。此外,它可能会根据编译器选项而改变。您可以做的最好的事情是打印每个变量的地址。然后就可以看到内存布局是怎样的了。

类似这样的:

int main(void)
{
  int num; 
  char s;   
  int *ptr;

  printf("num: %p - size %zu\n", (void*)&num, sizeof num);
  printf("s  : %p - size %zu\n", (void*)&s, sizeof s);
  printf("ptr: %p - size %zu\n", (void*)&ptr, sizeof ptr);

  return 0;
}

可能的输出:

num: 0x7ffee97fce84 - size 4
s  : 0x7ffee97fce83 - size 1
ptr: 0x7ffee97fce88 - size 8

另外请注意,如果您不获取变量的地址 (&),编译器可能会优化您的代码,以便该变量根本不会放入内存中。

一般而言,对齐通常是为了从所使用的硬件平台中获得最佳性能。这通常意味着变量与其大小对齐,或者对于大小大于 4 的变量,至少对齐 4 个字节。

更新:

OP 在更新中给出了一个特定的布局示例,并询问该布局是否可以/将会发生。

同样的答案是:依赖于实现

所以原则上它可能发生在某些特定系统上。也就是说,我怀疑它会在任何主流系统上发生。

还有一个用gcc -O3

编译的代码示例
int main(void)
{
  short s1;
  int i1;
  char c1;
  int i2;
  char c2;


  printf("s1: %p - size %zu\n", (void*)&s1, sizeof s1);
  printf("i1: %p - size %zu\n", (void*)&i1, sizeof i1);
  printf("c1: %p - size %zu\n", (void*)&c1, sizeof c1);
  printf("i2: %p - size %zu\n", (void*)&i2, sizeof i2);
  printf("c2: %p - size %zu\n", (void*)&c2, sizeof c2);

  return 0;
}

我系统的输出:

s1: 0x7ffd222fc146 - size 2   <-- 2 byte aligned
i1: 0x7ffd222fc148 - size 4   <-- 4 byte aligned
c1: 0x7ffd222fc144 - size 1
i2: 0x7ffd222fc14c - size 4   <-- 4 byte aligned
c2: 0x7ffd222fc145 - size 1

请注意内存中的位置与代码中定义的顺序变量有何不同。这确保了良好的对齐。

按地址排序:

c1: 0x7ffd222fc144 - size 1
c2: 0x7ffd222fc145 - size 1
s1: 0x7ffd222fc146 - size 2   <-- 2 byte aligned
i1: 0x7ffd222fc148 - size 4   <-- 4 byte aligned
i2: 0x7ffd222fc14c - size 4   <-- 4 byte aligned

所以再次回答更新问题:

在大多数系统上,我怀疑你会看到一个 4 字节的变量被放置在地址 xxx2、xxx6 或 xxxa、xxxe。但是,系统可能存在于可能发生这种情况的地方。

【讨论】:

  • 我的意思是,为什么在链接中,人们说边界是 4 个字节?
  • @Anni_housie 嗯,这很大程度上归功于硬件架构。例如,缓存通常以 2^N 字节的行组织。对于性能而言,如果一个 4 字节变量在一个缓存行中有两个字节,而在下一个缓存行中有接下来的两个字节,那将是很糟糕的。因此,我们通常希望 4 字节变量是 4 字节对齐的,以便变量可以保存在单个高速缓存行中。这只是一个例子——还有更多。但同样 - 它依赖于实现
  • 在许多 32 位架构中,处理器喜欢一次获取 32 位。如果数据项跨越 4 字节(32 位)边界,处理器将不得不进行两次提取,这会减慢程序的速度。通过保持数据对齐到 4 个字节,处理器只需进行一次提取。与缓存线没有任何关系,因为大多数缓存线都大于 4 字节。
  • @ThomasMatthews - 您的示例是以最佳方式使用硬件的另一个很好的示例。但是当你说缓存不是问题时你错了。如果一个 4 字节变量被放置在内存中,以便 1 个字节映射到 1 个缓存行,接下来的 3 个字节映射到下一个缓存行,则处理器在读取变量时将有 2 个缓存未命中(如果它不在缓存中已经,当然)
  • @4386427,关于您所说的“这通常意味着变量与其大小或至少 4 个字节”对齐,我还有 1 个问题。所以如果变量有不同的大小,我们是否仍然使用一个恒定长度的字边界(比如 4 个字节)?在这种情况下,“变量与其大小对齐”是什么意思?你的意思是我们可以有一个不固定的词边界长度?
【解决方案2】:

很难准确预测,但肯定会有一些填充。 以这两个代码为例(我在Coliru,64位机器上运行它们)

    #include<iostream>
#include <vector>
using namespace std;

//#pragma pack(push,1)
int main(){    
      int num1(5);  // 4byte
      int num2(3);   // 4byte
      char c1[2];
      c1[0]='a';
      c1[1]='a';
      cout << &num1 << " " << &num2 << " "  << endl;     
      cout << sizeof(c1) << " " << &c1 << endl;

}
//#pragma pack(pop)




    #include<iostream>
#include <vector>
using namespace std;

//#pragma pack(push,1)
int main(){    
      int num1(5);  // 4byte
      int num2(3);   // 4byte
      char c1[1];
      c1[0]='a';
      cout << &num1 << " " << &num2 << " "  << endl;     
      cout << sizeof(c1) << " " << &c1 << endl;

}
//#pragma pack(pop)

第一个程序输出:

0x7fff3e1f9de8 0x7fff3e1f9dec 
2 0x7fff3e1f9de0

当第二个程序输出时:

0x7fffdca72538 0x7fffdca7253c 
1 0x7fffdca72537

您肯定会注意到在第一个程序中进行了填充,查看地址我们可以看到: 第一个程序:CHAR |字符 | 6 字节填充 |情报 | INT 第二个程序:CHAR |情报 |整数

所以对于基本问题,是的,默认情况下它可能是填充。 我还尝试使用 pragma pack 来避免填充,与 struct 的情况相比,我没有设法让它避免填充,因为输出完全相同。

【讨论】:

    猜你喜欢
    • 2018-07-26
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-19
    • 2014-08-05
    • 2012-01-11
    • 2012-12-24
    • 1970-01-01
    相关资源
    最近更新 更多