严格别名规则uint8_t缓冲区来构造

kir*_*dar 6 c

考虑我有一个带有位字段的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)

请注意,由于结构非常大,因此无法手动将缓冲区复制到结构(成员逐个成员).

我的理解是否正确?如果是这样,请提供我可以用来证明我的观点的标准文件.

use*_*733 6

你是对的。

\n\n

C17 草案 \xc2\xa7 6.5:

\n\n
\n
    \n
  1. 对象的存储值只能由具有以下类型之一的左值表达式访问:89)

    \n\n
      \n
    • 与对象的有效类型兼容的类型,

    • \n
    • 与对象的有效类型兼容的类型的限定版本,

    • \n
    • 与对象的有效类型相对应的有符号或无符号类型,

    • \n
    • 与对象的有效类型的限定版本相对应的有符号或无符号类型,\n

    • \n
    • 在其成员中包含上述类型之一的聚合或联合类型\n(递归地包括子聚合或包含的联合的成员),或者
    • \n
    • 一种字符类型。
    • \n
  2. \n
\n
\n\n

在这种情况下,对象的类型为uint8_t[],左值表达式的类型为Message。上述任何例外情况均不适用。

\n\n

使用memcpy过度解引用可以解决此问题,或者如果您想用非 C 语言编写,则可以在编译器中禁用严格别名。

\n\n

但即使在此之后,代码仍然存在很多问题并且不可移植:位域在标准中的定义非常糟糕,并且整体结构是处理序列化的非常笨拙的方式。您应该选择手动反序列化每个成员。

\n

  • @Lundin 但另一方面是程序员没有系统经验,违反严格别名会导致实际问题。因此,你会遇到这样的问题,一群显然对严格别名一无所知的程序员似乎促使那些“确实”理解其含义的人在 Stackoverflow 上发布了一个问题。 (2认同)

Lun*_*din 5

我曾建议建议的方法不遵循严格的别名规则

正确.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].

此代码也不包含复制,并且移位将转换为机器代码中的相关位访问.