【问题标题】:This union statement fails with a bus error此联合语句因总线错误而失败
【发布时间】:2021-11-17 01:51:37
【问题描述】:

这会产生一个总线错误:

union { char a[10];
        int i;
      } u;
int *p = (int *) &u.a[1]);
*p = 17;

为什么会产生错误?我的意思是,chars 可以容纳数字 17。

【问题讨论】:

  • 第 4 行末尾还有额外的 )。你到底想做什么?
  • 什么是chars?这不在您的代码中。
  • "chars 可以容纳数字 17",这是真的,但是只要假定该值的类型为 int,编译器就会将所有四个字节写入那里(因此,假设 int为 4 个字节,0x00000011.)
  • @RobertHarvey 您需要在有对齐限制的处理器上运行。 x86 处理器可以让您摆脱 C 标准无法保证的事情。

标签: c unions bus-error


【解决方案1】:

u.a[1] 未正确对齐 int

通常,访问硬件的内存会一次获得很多位,例如 32 位或 64 位(四个或八个八位字节)。以 32 为例,在内存和处理器之间交换数据时,字节将按四字节组移动。例如,处理器将从内存中加载字节 1000、1001、1002 和 1003。

为了适应这种情况,处理器被设计成四字节整数总是位于四的倍数的地址。当程序想要从地址 1000 加载一个整数时,处理器会在单个事务中从内存中获取这些整数,该事务会获取字节 1000、1001、1002 和 1003,然后处理器将这些字节传递给寄存器。

为了获得单个字节,处理器仍然需要从内存中获得四个字节,但它可能只将请求的单个字节放入寄存器中。

如果联合u在地址1000,那么u.i从地址1000开始,u.a从1000开始,u.a[0]在1000,u.a[1]在1001,u.a[2]在1002,和u.a[3]在1003。当你将p设置为&u.a[1]时,它指向地址1001。当你使用*p时,程序试图从地址1001加载一个int。然后处理器产生一个异常,因为 1001 不是 int 的正确地址。

这些是基本的细节。实践中存在差异。一些处理器可能会成功地从 1001 加载 int,但它们会比对齐加载更慢,因为处理器必须从地址 1000 的内存中获取四字节字和地址 1004 的四字节字,并且然后从第一个单词中取出三个字节,从第二个单词中取出一个字节并将它们放在一起。在某些系统上,处理器仍会生成异常,但操作系统通过执行两次加载和合并来处理它,而不是通过向进程传递信号来处理它。

C 标准中涵盖此内容的规则在 C 2018 6.3.2.3 7 中:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义……

这实际上是说即使程序只是执行转换,行为也是未定义的,(int *) &u.a[1],但通常只有在尝试使用结果指针从内存加载或存储到内存时才会观察到异常。

【讨论】:

  • &u.a[0] 或者只是u.a 那么呢?还是整个事情都被误导了?
猜你喜欢
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 2013-01-15
  • 2023-03-10
  • 2016-06-02
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
相关资源
最近更新 更多