我想测试是否可以访问C中结构错误对齐的成员,请参阅代码
#include <stdio.h>
#pragma pack(1) /* force 1 byte alignment */
/* either member b or member d is mis-aligned */
typedef struct
{
int b;
unsigned char c;
unsigned int d;
}A ;
int main(int argc, char *argv[])
{
A _a = {0};
unsigned int *p = NULL;
unsigned int *q = NULL;
printf("addr of _a : 0x%08x, size of _a : %u\n", &_a, sizeof(_a));
p = (unsigned int*)(&_a.b);
q = (unsigned int*)(&_a.d);
/* should this fail ? */
(*p)++ , (*q)++;
return 0;
}
假设由于错误对齐的内存访问导致异常导致程序崩溃,但事实证明它运行良好,在Linux 3.6.11(GCC 4.7.2),WinXP(MingW)中进行了测试,键盘在线编译器(http://codepad.org/yOoc8ACG)
请解释结果,我想操作系统已经做了一些事情来保存程序,仍然怀疑它是否适用于VxWorks或其他一些操作系统
注意:代码在基于Intel的计算机上运行!
提前感谢!
答案 0 :(得分:1)
据推测,您在英特尔机器上运行。 x86架构可以毫无问题地处理未对齐的访问。但是,您可能在其他体系结构上遇到问题。即使架构碰巧不支持错位访问,你也是对的,有时操作系统可以通过在某种CPU异常处理程序中使用单字节访问来模拟错位访问来为你解决这个问题。
我认为您的测试计划中也需要(*p)++
和(*q)++
。
答案 1 :(得分:1)
上面的一些答案说“因为它是x86,它不会失败”,这不完全正确。虽然我知道没有操作系统可以做到这一点,但是可以将x86处理器配置为在用户模式下的未对齐访问时出错[不在内核模式,但发布的代码看起来像用户模式代码]。但就像我说的那样,我知道没有一个实际配置这个位的操作系统,并且它很可能会破坏一些不期望处理器因未对齐的内存访问而出错的代码。
未对齐访问的行为,无论您如何查看,都是未定义的或至多是实现定义的。这意味着这种操作的结果范围从“它在光谱的一端就像你期望的那样工作”到“程序崩溃”。在该范围的中间可能是更糟糕的选择“它不会崩溃,但它也不会像你期望的那样” - 例如,你可能会发现你得到的值与之前对齐的地址相对应[较低的内存地址]您希望获取的数据,或者确实正确执行了获取,但它比对齐的变体需要10或100倍的时间,因为处理器捕获并在几个步骤中执行操作,然后从陷阱。如果你也在运行多个线程,你也可能会发现变量的更新不是以原子方式完成的,所以你得到的值是“一半,另一半”,这显然会导致非常奇怪的效果。这不是“事情有点不对,但不是立即显而易见”的潜在情景的结论性清单。
我的建议是:不要搞乱对齐,尽最大努力永远不要编写访问未对齐元素的代码。它可能会回来并迟早咬你......
答案 2 :(得分:0)
基于x86的体系结构很不寻常,因为它们的字访问指令可用于未对齐的地址。结果操作较慢且非原子,但它可以工作。
只要您的程序包含#pragma
,其含义就是(最好)实现定义。一般来说,将数据成员的地址分配给unsigned int*
变量将使实现“忘记”它可能是错误对齐的,因此它不会为未对齐的加载发出代码。因此,对于重要的架构,*p
或*q
(或两者)都不起作用。
答案 3 :(得分:0)
结果取决于体系结构和内核配置。通常在x86上,您可以访问未对齐的数据,但会有一些性能损失。这主要是由于与旧系列CPU的兼容性。
在ARM和SPARC上,行为取决于内核配置。 OS可以禁止,允许甚至模拟这种未对齐的数据访问。在后一种情况下,内核拦截硬件异常,并使用某些操作系统“代码安静”进行模拟。
随着编译器版本的到位,这变得更加困难。例如,现代GCC生成一个特殊代码,如果它看到数据未对齐,则可以非原子方式(即使用多个指令)访问未对齐数据。