位域和序列点

Pas*_*uoq 17 c c99 sequence-points bit-fields c11

对于打包f0f1放入同一字节的实现,下面的程序是定义的吗?

struct S0 {
       unsigned f0:4;
       signed f1:4;
} l_62;

int main (void) {
       (l_62.f0 = 0) + (l_62.f1 = 0);
       return 0;
}
Run Code Online (Sandbox Code Playgroud)

我对C99和C11的答案感兴趣,如果有理由认为它在那里是不同的.

在C99中,我发现的只有6.5:2:

在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估来修改一次.[...]

我不清楚这一段对上述计划有何影响.

基于大量随机测试,大多数编译器似乎生成代码,其中两个分配不会干扰.

Jen*_*edt 3

C11 认为相邻的命名位字段是同一内存位置的一部分。此类位字段不保证以原子方式更新,换句话说,如果一个更新未在另一个更新之前显式排序,则行为未定义。3.14 memory location然后还详细解释了何时可以认为两个字段位于不同的内存位置,因此可以独立考虑对它们的更新。

如果你想修改你的结构

struct S0 {
       unsigned f0:4;
       int :0;
       signed f1:4;
} l_62;
Run Code Online (Sandbox Code Playgroud)

这样,两个位字段之间就有这个奇怪的“内存位置分隔符”,您的代码就可以保证没问题。

对于C99来说情况似乎更复杂,没有这么详细的内存位置概念。在最近关于 Linux 内核邮件列表的讨论中,有人声称,通常对于所有位字段对,在更新其中任何一个位字段时都会保证原子性。该讨论的出发点是 gcc 以意想不到的方式污染了与位字段相邻的非位字段,从而导致虚假崩溃。

  • 毫无疑问,修改相同的内存位置在 C11 中会引发数据竞争。但是 5.1.2.4/4 涉及内存位置,而 6.5/2 只涉及标量对象。我不确定这是一个疏忽。C++ 中的相应措辞类似,数据竞争,但显然不是单线程中单个表达式的 UB。 (2认同)