在C ++中声明,操作和访问未对齐的内存

时间:2016-10-07 17:00:06

标签: c++ memory undefined-behavior memory-alignment

我最近发布了一个关于unaligned memory access的问题,但鉴于答案,我有点迷失。我经常听到“对齐内存访问比未对齐访问更有效”,但我实际上不确定什么是未对齐内存。因此:

  • 什么是未对齐的记忆?
  • 如何在C ++中声明未对齐的内容? (小例程序)
  • 如何访问和操作C ++中未对齐的内容? (小例程序)
  • 是否有办法用定义的行为方法操作未对齐的内存,或者所有这些都是C ++中依赖于平台的/未定义的行为?

2 个答案:

答案 0 :(得分:4)

某些东西是否未对齐取决于数据类型及其大小正如Gregg的答案所解释的那样。

编写良好的程序通常没有未对齐的内存访问权限,除非编译器引入它。 (是的,这在矢量化期间发生,但让我们跳过它)。

但是你可以用C ++编写程序来强制进行未对齐的内存访问。下面的代码就是这样。

#include <iostream>
using namespace std;
int main() {

  int a[3] {1, 2, 3};

  cout << *((long long *)(&a[0])) << endl;
  cout << *((long long *)(&a[1])) << endl;

  cout <<  (long long) (&a[0]) << endl;
  cout << (long long) (&a[1]) << endl;

  return 0;

}

代码的输出是

8589934593
12884901890
70367819479584
70367819479588

此计划的作用是什么? 我声明了一个大小为3的整数数组。这个数组将是4字节对齐的,因为int是一个4字节的数据类型(至少在我的平台上)。因此,[0]的地址可以被4整除。现在,[0]和[1]的地址都可被4整除,但只有其中一个的地址可以被8整除。

因此,如果我将[0]和[1]的地址转换为指向long long的指针(在我的平台上是一个8字节的数据类型),然后将这两个指针放在一起,其中一个将是一个未对齐的内存访问。这不是未定义的行为AFAIK,但它会比对齐的内存访问慢。

如您所见,此代码包含C样式转换,这不是一个好习惯。但我觉得要强制执行一些很好的奇怪行为。

如果您对代码的输出有疑问,请告诉我。您应该了解字节序列和整数表示,以了解前两行。第三行和第四行是整数数组的前两个元素的地址。这应该更容易理解。

答案 1 :(得分:0)

以32位计算机读取4字节数据字为例:

在硬件中,32位计算机一次读取4个字节,但仅在每4个字节读取一个字节。这是因为内存总线宽度为4个字节。

如果您的4字节数据没有在这4个字节边界之一上启动,则计算机必须读取内存两次,然后在内部将4个字节组合到一个寄存器中。

基于所选择的体系结构,编译器知道这一点并放置/填充数据结构,使得两个字节数据出现在两个字节边界上,4个字节数据从4个字节边界开始,等等。这特别是为了避免错误对齐的读取

如果以字节(例如从串行协议)读取数据,然后以32位字的形式访问它们,则可能会出现未对齐的读取。在速度关键代码中避免这种情况通常它会照顾你,而不是问题。