在 u8 中铸造 u16[] 时如何强制对齐错误?

How to force an alignment error when casting a u8[] in a u16?

本文关键字:何强制 对齐 错误 u8 u16      更新时间:2023-10-16

在回答问题时,我试图警告OP注意对齐问题。

但是当我做我的片段来向 OP 展示它是如何发生的时,我无法让它发生。

在联机编译器上运行此代码 (C/C++) 时,我希望它会失败

为什么不是呢?

#include <cstdint>
#include <cstddef>
#include <iostream>
#define SIZE 20
int main()
{
uint8_t in[20];
in[0] = 0;
in[1] = 1;//8bit
in[2] = 1;
in[3] = 1;//16bit
in[4] = 1;
in[5] = 1;
in[6] = 1;
in[7] = 1;//32bit
in[8] = 1;
in[9] = 1;
in[10] = 1;
in[11] = 1;
in[12] = 1;
in[13] = 1;
in[14] = 1;
in[15] = 1;//64bit
in[16] = 1;
in[17] = 1;
in[18] = 1;
in[19] = 1;
uint16_t out;
for (int i =0; i < SIZE - 2; i++)
{
out = *((uint16_t*)&in[i+1]);
std::cout <<  "&in: " << (void*)&in[i+1] <<  "n out: " << out << "n in: " << in[i+2]*256 + in[i+1]<< std::endl;
}
return 0;
}

运行此代码时,我希望它会失败。为什么不是呢?

因为:

  1. 程序的行为未定义1.不能保证失败2.
  2. 您可能正在使用其 CPU 支持未对齐访问的系统。据我了解,例如 x86 执行未对齐的读取和写入;它们只是比对齐的慢(但这不适用于 SIMD 指令)。

C++标准说(引用最新草案):

1

[基本.lval]

如果程序尝试通过类型与以下类型之一不相似的 glvalue ([conv.qual]) 访问对象的存储值,则行为是未定义的

  • 对象的动态类型,
  • 与对象的动态类型对应的有符号或无符号类型,或
  • 字符、无符号字符或 std::byte 类型。

在这种情况下,uint16_t不是列出的异常类型(好吧,它可能在某些具有 16 位字节的系统上,但不是一般的,也可能不在运行在线编译器的服务器上,这样的系统可能不会提供uint8_t)。

阿拉伯数字

[defns.undefined]

本文档不强加要求的行为

请注意,缺乏任何保证。

运行此代码时,我希望它会失败。为什么不是呢?

由于严格别名冲突,程序具有未定义的行为,但这并不意味着它有义务失败(请参阅"未定义")。从对齐角度来看,通过没有其目标类型的自然对齐的指针访问值必须失败,尽管这是一种主要属于严格锯齿规则保护伞的情况。 此类访问尝试是否实际失败通常取决于运行程序的硬件。

具体发生的情况取决于所使用的平台(CPU 架构和操作系统)。

有几种可能性:

  1. 该体系结构根本没有自然的单词对齐方式,因此所有访问都被视为对齐。

  2. CPU 通过执行多个对齐的访问并构造结果(慢)在内部处理未对齐的访问。

  3. CPU 检测到未对齐的访问并引发异常。操作系统捕获此异常并模拟软件中的未对齐访问(速度较慢!

例如,Linux为多个arm架构提供了此选项,甚至可以选择此选项,如果应忽略,修复或发出未对齐的访问,则可以选择它,可以选择在内核日志中伴随警告(请参阅内核源文件arch/arm/mm/alignment.c

  1. 对齐会导致 CPU 异常,并且进程发出信号。 在 Linux 上,在这种情况下,该过程通常以SIGBUS终止。

摘要:避免未对齐的访问是安全的一面,但在大多数平台上,它仍然可以以一种或另一种方式工作。