C 访问错误对齐的结构成员

C 访问错误对齐的结构成员,c,compiler-construction,operating-system,access-violation,memory-alignment,C,Compiler Construction,Operating System,Access Violation,Memory Alignment,我想测试是否可以访问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

我想测试是否可以访问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;

}
#包括
#pragma包(1)/*强制1字节对齐*/
/*成员b或成员d未对齐*/
类型定义结构
{
int b;
无符号字符c;
无符号整数d;
}A;
int main(int argc,char*argv[])
{
A_A={0};
无符号int*p=NULL;
无符号int*q=NULL;
printf(“地址a:0x%08x,大小a:%u\n”,和a,大小f(_a));
p=(无符号整数*)(&u a.b);
q=(无符号整数*)(&_a.d);
/*这会失败吗*/
(*p)++,(*q)++;
返回0;
}
假设程序将由于错误对齐内存访问导致的异常而崩溃,但结果表明它运行得相当好,已经在Linux 3.6.11(GCC 4.7.2)、WinXP(MingW)、codepad online compiler()中进行了测试

请解释结果,我猜操作系统已经做了一些事情来保存程序,仍然怀疑它是否在VxWorks或其他操作系统中工作

注意:代码在基于Intel的机器上运行


提前谢谢

您大概是在英特尔计算机上运行的。x86体系结构可以毫无问题地处理未对齐的访问。不过,在其他体系结构上可能会有问题。即使某个体系结构恰好不支持未对齐的访问,您也可以说,有时操作系统可以通过在某种CPU异常处理程序中模拟单字节访问未对齐的访问来为您解决这一问题


我认为您也希望在测试程序中使用
(*p)+
(*q)+

基于x86的体系结构是不寻常的,因为它们的字访问指令可以在错误对齐的地址上使用。由此产生的操作速度较慢且不具有原子性,但它可以工作


只要您的程序包含一个
#pragma
,它的含义(充其量)就是定义的实现。一般来说,将数据成员的地址分配给
unsigned int*
变量将使实现“忘记”它可能未对齐,因此它不会为未对齐的加载发出代码。因此,在重要的体系结构上,
*p
*q
(或两者)都不起作用。

结果取决于体系结构和内核配置。通常在x86上,您可以访问未对齐的数据,但会带来一些性能损失。这主要是由于与旧的CPU系列兼容

在ARM和SPARC上,行为取决于内核配置。操作系统可以禁止、允许甚至模拟这种未对齐的数据访问。在后一种情况下,内核会截获硬件异常,并用一些操作系统的代码进行仿真


随着编译器版本的出现,这变得更加困难。例如,现代GCC生成一种特殊代码,如果它发现数据未对齐,则可以以非原子方式(即使用多条指令)访问未对齐的数据。

上面的一些答案说“因为它是x86,所以不会失败”,这并不完全正确。虽然我知道没有操作系统能做到这一点,但可以将x86处理器配置为在用户模式下(不是在内核模式下,但发布的代码在我看来就像是用户模式代码)的未对齐访问时出错。但正如我所说,据我所知,没有一个操作系统真正配置了这一位,它很可能会破坏一些不希望处理器因未对齐内存访问而出错的代码

未对齐访问的行为,无论您如何看待它,都是未定义的,或者充其量是实现定义的。这意味着,这种操作的结果范围从频谱一端的“它的工作方式与您预期的一样”到“程序崩溃”。在这个范围的中间可能是更糟糕的选项:“它不会崩溃,但它也不像你所期望的那样表现得很好”。例如,你可能会发现,你得到的值对应于对齐地址,就在你所要取的数据之前(或更低的内存地址),或者确实执行正确。但由于处理器陷波并分几个步骤执行操作,然后从陷波中返回,因此它需要比对齐的变体长10到100倍的时间。如果您同时运行多个线程,您可能还会发现变量的更新不是以原子方式完成的,因此您得到的值是“一个的一半,另一个的一半”,这显然会导致非常奇怪的效果。这并不是“事情有点不对劲,但不是以一种立即明显的方式”的潜在情景的结论性列表


我的建议是:不要搞对齐,并且尽最大努力不要编写访问未对齐元素的代码。它迟早会回来咬你的

如果在递增p和q之后尝试将它们传递给另一个函数,会发生什么?或者尝试访问这些位置的内存?行为的可能重复未定义;根据编译器和体系结构的不同,它可能会崩溃、产生不好的结果或“按预期”工作。请参阅和。一个次要问题:最好避免定义以下划线开头的标识符。有些保留给实现用于所有目的,其他保留给文件范围。