C 为什么一位'int'位字段的值为0和−;1.

C 为什么一位'int'位字段的值为0和−;1.,c,C,为什么这段代码会产生显示的输出 #include<stdio.h> struct sample{ int x:1; }a[5]={0,1,2,3,4}; int main(){ int i; for(i=0;i<5;++i){ printf("%d ",a[i].x); } return 0; } int x:1充当有符号一位整数。根

为什么这段代码会产生显示的输出

    #include<stdio.h>
    struct sample{
        int x:1;
    }a[5]={0,1,2,3,4};
    int main(){
        int i;
        for(i=0;i<5;++i){
            printf("%d ",a[i].x);
        }
        return 0;
    }

int x:1
充当有符号一位整数。根据C 2018 6.7.2 5,实现可能将声明为
int
的位字段视为
unsigned int
,但您使用的C实现似乎将其视为已签名

此外,C实现还使用了二的补码。在2的补码中,n位整数有1个符号位和n个−1个值位。因为整数是一位的,所以它有一个符号位和零值位。根据2的补码规范(C 2018 6.2.6.2 2),符号位的值为−2M,其中M是值位数。由于该值为零,符号位的值为−20 = −一,

因此,当位为零时,位字段的值为0−位为1时为1。1

在初始化过程中使用
={0,1,2,3,4},数组中的第一个位字段设置为零。但是,其他的溢出位字段中可表示的值。根据C 2018 6.3.1.3 3,要么产生实施定义的结果,要么发出实施定义的信号。此C实现似乎正在生成源值的低位作为位字段中位的值。这将在五个位字段中生成0、1、0、1和0的位,表示0的值,−1, 0, −1和0。因此,当打印它们时,值为0,−1, 0, −结果为1和0

脚注
1这可能被视为2的补码正常工作的自然结果:对于8位,范围为−128…127. 使用四位,范围为−16…15. 随着位越来越少,范围越来越小−8…7, −4…3, −2…1,最后−1…0。

< p>指定的int x:1表示它只考虑一个x(2 ^ 0比特)的位作为这个整数的范围。 这就解释了为什么输出是0和1。 负值是由于“int”引起的,通常保留最后一位(2^31)来指定数字是否为负值。您的in x:1有一个位,该位还表示它是负数

如果尝试“unsigned x:1”,它将输出一个正数,因为“x:int”的最后一位没有指定负数

使用“int x:2”和“unsigned x:2”尝试此程序,您会看到一个有趣的行为

它阐明了为什么int和unsigned的范围如此不同,尽管它们具有相同的位大小:

int->-2147483648至2147483647

未签名->0到4294967295


重要的是要注意“:”用于限制范围的位位置取决于实现,可能会给您带来其他结果。

首先,使用GCC 9.2.1,我看到了几个-Woverflow警告,对于每个初始值设定项,除了
0
1
(这很有意义)


-1
的初始值设定项也将在没有警告的情况下编译。这是因为它(定义为)一位字段。设置了所有(一)位的整数等于
-1
,除非它们是
无符号的
。LSB(第一位)从不设置为偶数,这解释了为什么只有奇数才是非零。

这是一个问答网站。问题是什么?当您对位字段使用有符号类型时,会发生这种情况。这是UB。@SouravGhosh:你凭什么声称这段代码中有未定义的行为?@SouravGhosh:大多数表达式中都没有定义有符号溢出。但转换中的签名溢出是由实现定义的(C 2018 6.3.1.3)。在
…a[5]={0,1,2,3,4},初始值设定项转换为目标类型。对位字段使用无符号整数。(2^0位)您无法断定哪些位由位字段表示。位字段完全依赖于实现。没错,但在这种情况下,输出中的负数是由于这种行为造成的。我想添加一个未提及的C怪癖:
int x:1
也可能作为一个无符号的一位整数,因为
int
的有符号性也是位字段的类型,也是实现定义的行为。
0 -1 0 -1 0