我可以相信每次访问位字段时C编译器都会模2 ^ n吗? 或者是否存在任何编译器/优化,其中下面的代码不会打印溢出?
struct {
uint8_t foo:2;
} G;
G.foo = 3;
G.foo++;
if(G.foo == 0) {
printf("Overflow\n");
}
先谢谢,Florian
答案 0 :(得分:12)
是的,你可以信任C编译器在这里做正确的事情,只要用无符号类型声明位字段,你就可以使用uint8_t
。根据C99标准§6.2.6.1/ 3:
存储在无符号位域中的值和unsigned char类型的对象应使用纯二进制表示法表示。 40)
来自§6.7.2.1/ 9:
位字段被解释为由指定位数组成的有符号或无符号整数类型。 104)如果值0或1存储在非零宽度位域中类型
_Bool
,位字段的值应等于存储的值。
从§6.2.5/ 9(强调我的):
有符号整数类型的非负值范围是相应无符号整数类型的子范围,每种类型中相同值的表示形式相同。 31) A涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数类型表示的结果将以模数的形式减少,该数字大于可由结果类型表示的最大值。
所以是的,你可以确定任何符合标准的编译器都会G.foo
溢出到0而没有任何其他不必要的副作用。
答案 1 :(得分:3)
没有。编译器为字段分配2位,递增3导致100b,当置于两位时导致0。
答案 2 :(得分:0)
简短回答:是的,你可以信任模2 ^ n发生。
在你的节目中,
G.foo++;
实际上等同于G.foo = (unsigned int)G.foo + 1
。
无符号整数运算总是产生2 ^(无符号整数位的int)结果。然后将这两个最小权重的比特存储在G.foo
中,产生零。
答案 3 :(得分:0)
是的。我们可以从 assembly 获得答案。 这是我在Ubuntu 16.04、64位,gcc中编写代码的示例。
#include <stdio.h>
typedef unsigned int uint32_t;
struct {
uint32_t foo1:8;
uint32_t foo2:24;
} G;
int main() {
G.foo1 = 0x12;
G.foo2 = 0xffffff; // G is 0xfffff12
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
G.foo2++; // G.foo2 overflow
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
G.foo1 += (0xff-0x12+1); // // G.foo1 overflow
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
return 0;
}
使用gcc -S <.c file>
进行编译。您可以获取程序集文件.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
我们可以看到编译器将使用移位指令来确保您所说的内容。(注意: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