C 位域溢出

C 位域溢出,c,overflow,bit-fields,C,Overflow,Bit Fields,每次我访问一个位字段时,我能相信C编译器会对2^n进行模运算吗? 或者,是否有任何编译器/优化程序,使下面的代码不会打印溢出 struct { uint8_t foo:2; } G; G.foo = 3; G.foo++; if(G.foo == 0) { printf("Overflow\n"); } 提前感谢Florian,没有。编译器将2位分配给字段,增加3将得到100b,当放入两位时得到0。简短回答:是的,您可以相信模2^n会发生 在你的节目里,, G.foo++实际上相当

每次我访问一个位字段时,我能相信C编译器会对2^n进行模运算吗? 或者,是否有任何编译器/优化程序,使下面的代码不会打印溢出

struct {
  uint8_t foo:2;
} G;

G.foo = 3;
G.foo++;

if(G.foo == 0) {
  printf("Overflow\n");
}

提前感谢Florian,没有。编译器将2位分配给字段,增加3将得到100b,当放入两位时得到0。

简短回答:是的,您可以相信模2^n会发生

在你的节目里,,
G.foo++
实际上相当于
G.foo=(unsigned int)G.foo+1


无符号整数算术总是产生2^(以位为单位的无符号整数大小)的结果。然后,重量最小的两个位存储在
G.foo
中,产生零。

是的,只要位字段是用无符号类型声明的,就可以相信C编译器在这里做正确的事情,这与
uint8\t
有关。根据C99标准§6.2.6.1/3:

存储在无符号位字段和无符号字符类型对象中的值应使用纯二进制表示法表示。40)

根据§6.7.2.1/9:

位字段被解释为由指定位数组成的有符号或无符号整数类型。104)如果值0或1存储在
\u Bool
类型的非零宽度位字段中,则位字段的值应与存储的值进行比较

以及§6.2.5/9(重点):

有符号整数类型的非负值范围是相应无符号整数类型的子范围,并且每种类型中相同值的表示形式相同。31)涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将被减少为比结果类型可以表示的最大值大一的数。


因此,是的,您可以确保任何符合标准的编译器将
G.foo
溢出到0,而不会产生任何其他不必要的副作用。

是的。我们可以从汇编中得到答案。 下面是Ubuntu 16.04中的示例代码,64位,gcc

#包括
typedef unsigned int uint32\u t;
结构{
uint32:1:8;
uint32:2:24;
}G;
int main(){
G.foo1=0x12;
G.foo2=0xffffff;//G为0xfffff12
printf(“G.foo1=0x%02x,G.foo2=0x%06x,G=0x%08x\n”,G.foo1,G.foo2,*(uint32\u t*)&G);
G.foo2++;//G.foo2溢出
printf(“G.foo1=0x%02x,G.foo2=0x%06x,G=0x%08x\n”,G.foo1,G.foo2,*(uint32\u t*)&G);
G.foo1+=(0xff-0x12+1);///G.foo1溢出
printf(“G.foo1=0x%02x,G.foo2=0x%06x,G=0x%08x\n”,G.foo1,G.foo2,*(uint32\u t*)&G);
返回0;
}
使用
gcc-S
编译它。您可以获取程序集文件
.s
。这里我展示了
G.foo2++,我写了一些评论

movl    G(%rip), %eax
shrl    $8, %eax    #  0xfffff12-->0x00ffffff
addl    $1, %eax    # 0x00ffffff+1=0x01000000
andl    $16777215, %eax # 16777215=0xffffff, so eax still 0x01000000
sall    $8, %eax    # 0x01000000-->0x00000000
movl    %eax, %edx  # edx high-24bit is fool2
movl    G(%rip), %eax   # G.foo2, tmp123
movzbl  %al, %eax   # so eax=0x00000012
orl     %edx, %eax  # eax=0x00000012 | 0x00000000 = 0x00000012
movl    %eax, G(%rip)   # write to G
我们可以看到,编译器将使用shift指令来确保您所说的内容。(注意:G的内存布局如下:

----------------------------------
|     foo2-24bit     | foo1-8bit |
----------------------------------
当然,上述结果是:

G.foo1=0x12, G.foo2=0xffffff, G=0xffffff12
G.foo1=0x12, G.foo2=0x000000, G=0x00000012
G.foo1=0x00, G.foo2=0x000000, G=0x00000000

你是什么意思?你明确要求它以两位存储数据。另外,3+1==0 mod 4I更关心的是,如果在该结构中声明了其他位字段,那么该位会发生什么情况。例如,uint8_t bar:2;uint8_t foo:2;uint8_t poo:2;它会影响bar还是poo?Charles,它只使用2位进行存储,所以它只能存储0,1,2或3。你的等价物是错误的。
uint8\u t
将被提升为
int
,而不是
无符号int
G.foo++;
相当于
G.foo=((int)G.foo+1)%4;
@R.。你是对的(我总是把这些提升弄错),但上面Adam的答案中缺少了一点,+1实际上发生在有符号整数之间。正如R在下面的评论中向我指出的,计算实际上是有符号的计算。这不会改变答案,但它会,比如说,在有符号整数的体系结构上有一个31位无符号位字段32位(可能存在未定义的有符号溢出)。