考虑我有一个带有位字段的typedef,如下所示.
typedef struct VIN_oCAN01_3abd61be
{
uint64_t var1:24;
uint64_t var2:4;
uint64_t var3:4
}__attribute__((packed))Message;
Run Code Online (Sandbox Code Playgroud)
我收到uint8_t缓冲区如下.
uint8_t buffer[4] = {0x1,0x2,0x3,0x14};
Run Code Online (Sandbox Code Playgroud)
目前在我的制作计划中,我的团队建议采用以下方法.
Message *ptrMsg = (Message *)buffer;
Run Code Online (Sandbox Code Playgroud)
也就是说,将uint8_t缓冲区分配给Message类型指针.我曾建议提议的方法不遵循严格的别名规则,而是我们应该如下所示.
Message msg;
memcpy(&msg,buffer, sizeof(msg));
Run Code Online (Sandbox Code Playgroud)
请注意,由于结构非常大,因此无法手动将缓冲区复制到结构(成员逐个成员).
我的理解是否正确?如果是这样,请提供我可以用来证明我的观点的标准文件.
你是对的。
\n\nC17 草案 \xc2\xa7 6.5:
\n\n\n\n\n\n
\n- \n
对象的存储值只能由具有以下类型之一的左值表达式访问:89)
\n\n\n
- \n
与对象的有效类型兼容的类型,
- \n
与对象的有效类型兼容的类型的限定版本,
- \n
与对象的有效类型相对应的有符号或无符号类型,
- \n
与对象的有效类型的限定版本相对应的有符号或无符号类型,\n
- 在其成员中包含上述类型之一的聚合或联合类型\n(递归地包括子聚合或包含的联合的成员),或者
\n- 一种字符类型。
\n
在这种情况下,对象的类型为uint8_t[],左值表达式的类型为Message。上述任何例外情况均不适用。
使用memcpy过度解引用可以解决此问题,或者如果您想用非 C 语言编写,则可以在编译器中禁用严格别名。
但即使在此之后,代码仍然存在很多问题并且不可移植:位域在标准中的定义非常糟糕,并且整体结构是处理序列化的非常笨拙的方式。您应该选择手动反序列化每个成员。
\n我曾建议建议的方法不遵循严格的别名规则
正确.ptrMsg = (Message *)buffer意味着您无法在ptrMsg不调用未定义行为的情况下访问数据.
你可以用C176.5§7证明你的观点(这里引用 - 什么是严格的混叠规则?).左值表达式(例如,ptrMsg->var1 = value不通过与存储在其中的有效类型兼容的类型访问存储的值,也不通过任何允许的表达式).
但是,你可以从Message一个数组uint8_t(假设uint8_t是一个字符类型)而不违反严格的别名.
然而,更大的问题是首先是比特字段的先例,它是非标准的和非便携的.例如,您无法知道位域的哪个部分是MSB和LSB.您无法知道这些位如何在64位类型中对齐.对比特字段使用64位类型是非标准扩展.它依赖于endianess.等等.
假设24位是指第31位到第8位(通过读取代码我们无法知道),那么没有严格混叠违规,位字段疯狂和非标准"struct padding killers"的正确代码将如下所示:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Run Code Online (Sandbox Code Playgroud)
Message是"在其成员中包括上述类型之一的联盟类型".意思是它包含一个兼容的类型uint8_t [4].
此代码也不包含复制,并且移位将转换为机器代码中的相关位访问.