在c++中声明、操作和访问未对齐内存

Declare, manipulate and access unaligned memory in C++

本文关键字:访问 对齐 内存 操作 c++ 声明      更新时间:2023-10-16

我最近发布了一个关于未对齐内存访问的问题,但是给出了答案,我有点迷路了。我经常听到"对齐的内存访问远比未对齐的访问更有效",但我实际上不确定什么是未对齐的内存。结果:

  • 什么是未对齐内存?
  • 如何在c++中声明不对齐的东西?(小程序示例)如何在c++中访问和操作未对齐的东西?(小程序示例)
  • 是否有一种方法来操纵未对齐的内存与定义的行为方法或所有这些都是平台依赖/未定义的行为在c++中?

是否未对齐取决于数据类型和它的大小,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字节的数据类型(至少在我的平台上)。所以a[0]的地址能被4整除。现在a[0]和a[1]的地址都能被4整除,但只有其中一个地址能被8整除。

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

如您所见,这段代码包含C风格强制转换,这不是一个好的做法。但我认为强制一些奇怪的行为是可以的。

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

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

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

如果你的4字节数据不是从这些4字节边界中的一个开始,计算机必须读取内存两次,然后在内部将4字节汇编到一个寄存器中。

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

如果您以字节(如从串行协议)读取数据,然后将其作为32位字访问,则可能会获得不对齐的读取。在速度关键代码中避免这种情况。