简体   繁体   English

此联合语句因总线错误而失败

[英]This union statement fails with a bus error

This generates a bus error:这会产生总线错误:

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

Why would this generate an error?为什么这会产生错误? I mean, chars can hold the number 17.我的意思是, chars可以容纳数字 17。

ua[1] is not properly aligned for an int . ua[1]没有正确对齐int

Commonly, the memory that accesses hardware gets many bits at once, such as 32 or 64 bits (four or eight eight-bit bytes).通常,访问硬件的内存一次获得很多位,例如 32 或 64 位(四个或八个八位字节)。 Using 32 as an example, when exchanging data between memory and the processor, bytes will be moved in four-byte groups.以32为例,在内存和处理器之间交换数据时,字节会以四字节为一组进行移动。 So the processor would load bytes 1000, 1001, 1002, and 1003 from memory, for example.例如,处理器将从内存中加载字节 1000、1001、1002 和 1003。

To accommodate this, the processor is designed so that four-byte integers are always located at addresses that are multiples of four.为了适应这一点,处理器被设计成四字节整数总是位于四的倍数的地址处。 When the program wants to load an integer from address 1000, the processor gets those from memory in a single transaction that gets bytes 1000, 1001, 1002, and 1003, which the processor then delivers to a register.当程序想要从地址 1000 加载一个整数时,处理器会在单个事务中从内存中获取这些数据,该事务获取字节 1000、1001、1002 和 1003,然后处理器将这些字节传递给寄存器。

To get a single byte, the processor still has to get four bytes from memory, but it may put just the single byte requested in a register.要获取单个字节,处理器仍必须从内存中获取四个字节,但它可能只将请求的单个字节放入寄存器中。

If the union u is at address 1000, then ui starts at address 1000, and ua starts at 1000, with ua[0] at 1000, ua[1] at 1001, ua[2] at 1002, and ua[3] at 1003. When you set p to &u.a[1] , it points to address 1001. When you use *p , the program attempts to load an int from address 1001. Then the processor generates an exception, because 1001 is not a proper address for an int .如果联合u在地址 1000,则ui从地址 1000 开始, ua从 1000 开始, ua[0]在 1000, ua[1]在 1001, ua[2]在 1002, ua[3]在1003. 当您将p设置为&u.a[1] ,它指向地址 1001。当您使用*p ,程序会尝试从地址 1001 加载一个int 。然后处理器生成一个异常,因为 1001 不是一个正确的int地址。

These are the essential details.这些是必不可少的细节。 There are variations in practice.在实践中存在差异。 Some processors may successfully load an int from 1001, but they will do it more slowly than an aligned load, because the processor has to get the four-byte word from memory at address 1000 and the four-byte word at address 1004 and then take three bytes from the first word and one byte from the second and put them together.一些处理器可能会成功地从 1001 加载一个int ,但它们会比对齐加载更慢,因为处理器必须从地址 1000 的内存中获取四字节字和地址 1004 处的四字节字,然后取第一个字的三个字节和第二个字的一个字节并将它们放在一起。 On some systems, the processor still generates an exception, but the operating system handles it by doing the two loads and the merge instead of by delivering a signal to the process.在某些系统上,处理器仍然会生成异常,但操作系统通过执行两次加载和合并而不是向进程传递信号来处理它。

A rule in the C standard covering this is in C 2018 6.3.2.3 7: C 标准中涵盖此内容的规则在 C 2018 6.3.2.3 7 中:

A pointer to an object type may be converted to a pointer to a different object type.指向对象类型的指针可以转换为指向不同对象类型的指针。 If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined…如果结果指针没有与引用类型正确对齐,则行为未定义......

That actually says the behavior is undefined even if the program just performs the conversion, (int *) &u.a[1] , but an exception is often observed only when an attempt is made to use the resulting pointer to load from or store to memory.这实际上表示即使程序只是执行转换(int *) &u.a[1] ,行为也是未定义的,但只有在尝试使用结果指针加载或存储到时才会观察到异常记忆。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM