为了理解位字段存储器,我在下面创建了测试程序.
#include <stdio.h>
int main()
{
int a;
typedef struct {
int b7 : 1;
int b6 : 1;
int b5 : 1;
int b4 : 1;
int b3 : 1;
int b2 : 1;
int b1 : 1;
int b0 : 1;
} byte;
byte ab0 = {0,0,0,0,0,0,0,1};
a = *(int*)&ab0;
printf("ab0 is %x \n",a);
byte ab1 = {0,0,0,0,0,0,1,0};
a = *(int*)&ab1;
printf("ab1 is %x \n",a);
byte ab2 = {0,0,0,0,0,1,0,0};
a = *(int*)&ab2;
printf("ab2 is %x \n",a);
byte ab3 = {0,0,0,0,1,0,0,0};
a = *(int*)&ab3;
printf("ab3 is %x \n",a);
byte ab4 = {0,0,0,1,0,0,0,0};
a = *(int*)&ab4;
printf("ab4 is %x \n",a);
byte ab5 = {0,0,1,0,0,0,0,0};
a = *(int*)&ab5;
printf("ab5 is %x \n",a);
byte ab6 = {0,1,0,0,0,0,0,0};
a = *(int*)&ab6;
printf("ab6 is %x \n",a);
byte ab7 = {1,0,0,0,0,0,0,0};
a = *(int*)&ab7;
printf("ab7 is %x \n",a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译并运行
gcc -Wall test.c
./a.out
ab0 is 80
ab1 is 40
ab2 is 20
ab3 is 10
ab4 is 8
ab5 is 4
ab6 is 2
ab7 is 1
Run Code Online (Sandbox Code Playgroud)
当我在线运行代码http://codepad.org/ntqyuixp时输出相同
我无法理解它的输出.
预期输出: 根据我的理解,输出应该是这样的
ab0 is 1
ab1 is 2
ab2 is 4
ab3 is 8
ab4 is 10
ab5 is 20
ab6 is 40
ab7 is 80
Run Code Online (Sandbox Code Playgroud)
请让我知道我错过了什么.
字节序是否起任何作用?
如何为我预期的行为编写代码?
Jon*_*ler 10
位字段中的位的顺序是实现定义的.实现与您期望的定义有不同的定义 - 几乎所有关于它的定义.
关于位字段的几乎所有内容都是实现定义的.
4指定位字段宽度的表达式应为整数常量表达式,其非负值不超过指定类型的对象的宽度,冒号和表达式省略.122)如果该值为零,则声明不应具有声明者.
5位字段应具有一种类型,是一个合格的或不合格的版本
_Bool
,signed int
,unsigned int
,或一些其他实现定义类型.它是实现定义的,是否允许原子类型....
9结构或联合的成员可以具有除可变修改类型之外的任何完整对象类型.123)此外,可以声明成员由指定数量的位组成(包括符号位,如果有的话).这样的成员称为位域; 124)它的宽度前面有一个冒号.
10位字段被解释为具有由指定位数组成的有符号或无符号整数类型.125)如果将值0或1存储到类型的非零宽度位字段中
_Bool
,则位字段的值应比较等于存储的值; 一个_Bool
位字段的语义_Bool
.11实现可以分配任何足够大的可寻址存储单元来保存位域.如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中.如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的.单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的.未指定可寻址存储单元的对齐.
12没有声明符但只有冒号和宽度的位字段声明表示未命名的位字段.126)作为一种特殊情况,宽度为0的位域结构成员表示不再将其他位字段打包到放置了前一位域(如果有的话)的单元中.
122)虽然
_Bool
对象中的比特数至少是CHAR_BIT
,但是a的宽度(符号和值比特的数量)_Bool
可以仅为1比特.123)结构或联合不能包含具有可变修改类型的成员,因为成员名称不是6.2.3中定义的普通标识符.
124)一元
&
(地址)运算符不能应用于位字段对象; 因此,没有指向位域对象的指针或数组.125)如上面6.7.2中所规定的,如果使用的实际类型说明符是
int
或者定义为typedef-nameint
,则无论位字段是有符号还是无符号,它都是实现定义的.126)未命名的位字段结构成员对于填充符合外部强加的布局很有用.
特别注意11:
单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的.
另请注意,"实现定义"意味着实现必须定义它的功能.也就是说,您可以检查文档,文档必须告诉您编译器的作用(如果编译器符合标准).这与"未指定"以及您将遇到的其他一些术语不同 - 编译器编写器几乎肯定不会随意更改位字段从发布到发布的行为.相反,例如,评估函数参数的方式可能会在发布之间发生变化,或者根据编译时选择的优化选项等进行更改.
§6.5.2.2函数调用
在评估函数指示符和实际参数之后但在实际调用之前有一个序列点.调用函数(包括其他函数调用)中的每个评估(在执行被调用函数的主体之前或之后没有特别排序)对于被调用函数的执行是不确定地排序的.94)
94)换句话说,函数执行不会相互"交错".
6.7.2类型说明符
5每个以逗号分隔的多重集都指定相同的类型,但对于位字段,它是实现定义的,指定者是
int
指定相同类型signed int
还是相同类型unsigned int
.