比特存储在内存中的顺序

3

这是一台X86机器,操作系统为Linux 2.6红帽版。 以下是我的代码:

    #include "stdio.h"
    typedef struct ch_t
    {
        int c0:1;
        int c1:1;
        int c2:1;
        int c3:1;
        int c4:1;
        int c5:1;
        int c6:1;
        int c7:1;
    } ch;

    typedef union chh_u
    {
        char a;
        ch chat;
    } chh;
    int main(void)
    {
        chh uu;
        uu.a = 6;
        printf("\n%d", uu.chat.c0);
        printf("\n%d", uu.chat.c1);
        printf("\n%d", uu.chat.c2);
        printf("\n%d", uu.chat.c3);
        printf("\n%d", uu.chat.c4);
        printf("\n%d", uu.chat.c5);
        printf("\n%d", uu.chat.c6);
        printf("\n%d", uu.chat.c7);
        printf("\n%d\n", uu.a);
        return 0;
    }

正如我所预期的,输出应该是:

0 0 0 0 0 1 1 0 6

但实际输出为:

0 -1 -1 0 0 0 0 0 6

我不明白为什么会有以上的输出。

我认为6位顺序是0000 0110,在内存中,按照我的理解,位顺序也应该是0000 0110。但是输出结果显示不同。

有人可以解释一下吗?


@SouravGhosh 为什么不呢? - fuz
请不要将自己的类型命名为 something_t。名称以 _t 结尾的类型是保留给操作系统及其头文件使用的。 - fuz
在我看来,通常使用“%d”可能会遇到上述情况。在1位的情况下,它将考虑“signed”值。对于位域变量,最好始终使用“unsigned”类型。 - Sourav Ghosh
如果这是你的意思,那么你的评论是错误的。%dsigned int 的正确格式说明符。你可能需要重新编写你的评论。 - fuz
@FUZxxl 好的,现在我明白了。为了避免混淆,我会删除第一条评论。顺便说一句,谢谢。 - Sourav Ghosh
在位定义中使用'int'是不正确的,因为单个位不能被标记为有符号。所以请使用:'unsigned int'。 - user3629249
4个回答

4
相关标准的部分内容为6.7.2(5)。
每个用逗号分隔的集合指定相同类型,但对于位域来说,指定符int指定与signed int相同的类型还是与unsigned int相同的类型是由实现定义的。
这解释了为什么您可能会得到-1而不是1。另一个标准是6.7.2.1(10):
实现可以分配足够大以容纳位域的任何可寻址存储单元。如果有足够的空间,立即跟随结构中的另一个位域的位域将被打包到同一单元的相邻位中。如果剩余空间不足,则超出大小的位域是放入下一个位或重叠相邻单元是由实现定义的。位字段在单元内的分配顺序(高位到低位或低位到高位)由实现定义。可寻址存储单元的对齐方式未指定。
因此,这也可以两种方式进行。
补充说明:由于似乎存在一些混淆:如果函数期望内部为int,则使用变量参数列表调用带有位域的函数没问题,原因与使用以下语句没问题相同
char c = '\xff';
printf("%d\n", c); // will usually print 255 or -1

它可以出于完全相同的原因给出不同的结果,因为与位域一样,char 可以根据实现而有符号或无符号。我将引用相关部分,这次剪去了不相关的部分,因为这些规则被埋藏在 C99 标准的某些沉闷的法律文件中。它们可以在 6.5.2.2(6)中找到:
如果表示调用函数的表达式具有不包括原型的类型,则对每个参数执行整数提升,并将类型为 float 的参数转换为 double。这些称为默认参数提升。(...)
和 6.5.2.2(7):
(...) 函数原型声明符中的省略符符号会导致参数类型转换在最后一个声明的参数之后停止。尾随参数执行默认参数提升。
整数提升在 6.3.1.1 中定义;相关部分在第 2 和 3 段中:
以下内容可在任何可以使用 intunsigned int 的地方使用:
- (...) - 类型为 _Boolintsigned intunsigned int 的位域。
如果 int 可以表示原始类型的所有值,则将该值转换为 int;否则,它将转换为 unsigned int。(...)
整数提升保留值,包括符号。
所以,这个过程发生了:
- 您的编译器将 int 位域视为有符号的。 - 这意味着宽度为 1 的位域其位设置为 -1;换句话说,只有符号被设置。 - 在传递给 printf 时,它被转换为 int,保留此值。 - 此 int 按照 %d 格式字符串由 printf 打印,并输出 "-1"。 - 获利?

非常感谢,我明白了。 - user3819055

1
尝试将位域中的值设为unsigned。这样可以避免出现意外行为。
typedef struct ch_t {
    unsigned int c0:1;
    unsigned int c1:1;
    unsigned int c2:1;
    unsigned int c3:1;
    unsigned int c4:1;
    unsigned int c5:1;
    unsigned int c6:1;
    unsigned int c7:1;
} ch;

0

在这里,您正在创建一个 signed int [通常情况下,int 默认为 signed int] 类型的 bitfield 变量,并尝试使用 %d 格式说明符打印该值。不幸的是,这将无法为单位 bit 的 bitfields 提供正确的值。

正如 @wintermute 在他的评论中澄清的那样,在直接使用 bitfielded 变量时,它们被视为 signed integer。另一方面,根据规则,单个 bit 的 signed integer 可以具有 0-1 的值,因此,在这种情况下,表示将是 -1

此外,在您的情况下,我假设您了解 c0LSB,而 c7 将是 MSB,对吗?

在打印之前,您需要取出所需的 bit。尝试像下面这样的东西。

#include <stdio.h>
typedef struct ch_t
{
        int c0:1;
        int c1:1;
        int c2:1;
        int c3:1;
        int c4:1;
        int c5:1;
        int c6:1;
        int c7:1;
} ch;

typedef union chh_u
{
        char a;
        ch chat;
} chh;
int main(void)
{
        chh uu;
        uu.a = 6;
        printf("\n%d", (uu.chat.c0) && 0x01);
        printf("\n%d", (uu.chat.c1) && 0x01);
        printf("\n%d", (uu.chat.c2) && 0x01);
        printf("\n%d", (uu.chat.c3) && 0x01);
        printf("\n%d", (uu.chat.c4) && 0x01);
        printf("\n%d", (uu.chat.c5) && 0x01);
        printf("\n%d", (uu.chat.c6) && 0x01);
        printf("\n%d", (uu.chat.c7) && 0x01);
        printf("\n%d\n", uu.a);
        return 0;
}

否则,在您的结构定义中使用无符号整数(unsigned int)数据类型。

1
位域在可变参数列表中被转换为int。他得到的值很好,只是被他的编译器视为有符号数,并扩展为一个值为-1的int。 - Wintermute
@Wintermute 没错,我会在我的回答中加入这个参考,如果你允许的话。 :-) - Sourav Ghosh
非常感谢您的回答。:-) - user3819055

0
在结构体ch_t中,位域c0..c7占据了低8位,其中c0是最低的(2^0),c7是最高的(2^7)。按照从高到低的顺序排列,就像写二进制数一样,你有c7 c6 c5 c4 c3 c2 c1 c0。八个二进制数字中的数字6是00000110,这意味着c2c1包含1位,其余包含0位。这与您得到的结果匹配,其中这些字段显示为-1
它们显示为-1而不是1的原因是,您使用了int而不是unsigned int作为您的位域。这实际上给您提供了一个1位的带符号整数类型,对于补码只能容纳0或-1。

感谢您的回答。 - user3819055

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接