指向 struct 的指针可以为 unsigned char 数组起别名吗?

Can a pointer to struct alias an unsigned char array?

#include "stdio.h"  


/* array to store data receeived from CAN Bus */
unsigned char a[8] = {0xCD, 0xEF, 0x12, 0x34, 0x50, 0x00, 0x00, 0x00};

typedef struct {
    unsigned int a:12;
    unsigned int b:12;
    unsigned int c:12;
    unsigned int unused:28;
}test;

test *tptr;

int main(void)
{

    tptr = (test*)( (void*)&a); // is this line braking any aliasing rule

    if(tptr->a == 0xCDE)
    {
        printf("\n data received ok");

    }

    return 0;
}

我最近了解到由于 C 中的指针别名导致的问题。我想知道上面的代码是否违反了任何规则。它会导致问题吗?

我知道位域的顺序取决于机器和实现。然而,我的问题是关于指针别名规则,我想更清楚地理解它

它确实打破了严格的别名。一些(理智的)编译器确实推断出显然 tptr 应该被允许使用别名 a,但是标准并不能保证这一点。事实上,GCC 不会将 tptr 视为别名 a.

另一个问题是a的对齐。 x86 在这方面相当宽容,但许多其他架构(包括 ARM)则不然。

考虑 (int)&a == 0xFFF02sizeof(int) == 4 架构支持仅从本身是 sizeof(int) 的倍数的地址获取 ints。 这样的 unaligned access 会导致总线错误,如果你这样做 *(int*)&a 甚至更糟的是程序访问了错误的位置。

为了避免未定义的行为,请记住可以保证通过 unsigned char* 的访问可以在没有对齐或别名约束的情况下进行,而不是相反。因此使用:

test buf;
can_recv((unsigned char*)&buf, sizeof buf);
if(tptr->a == 0xCDE)
{
    printf("\n data received ok");

}

另一个可移植性问题是,您希望该位域以某种方式打包。 The standard doesn't guarantee that however。如果您想要可移植的行为,请将自己限制在位域接口 (bitfield.b1 = 1) 并且不要通过其他方式修改位域。

如果可移植性不是问题,请确保设置适当的编译器标志以保证您期望的位域打包。