“ 在C语言中我们可以使用位域(位段)的方式让一个变量占据的位数少于8位,或占据位数不为8的整数倍;字节序决定了多字节数据在内存中的存储方式,与此同时字节序也可能会决定位域的实现方式。 ”
下面我会先讲解位域和字节序的一些基础知识,然后结合自己的经验讲讲不同字节序下的位域的区别,当然基础比较扎实的朋友可以直接跳过前两节。
位域(bit field)又称位段,它将允许你将单个字节分割成若干个字段,达到节省存储空间的目的。
例如,我们有四个变量a、b、c、d,每个变量均只有两种状态,在使用位域后,它们可以被压缩放入一个字节当中,此时的声明应该为:
#include "stdio.h"
#include "stdint.h"
struct TestBitField_s
{
uint8_t a : 1;
uint8_t b : 1;
uint8_t c : 1;
uint8_t d : 1;
};
int main()
{
struct TestBitField_s testBitField;
printf("%ld\n", sizeof(testBitField));
// 输出为:1
}
该程序输出的结果为1,代表我们4个变量只占用了一个字节。
在位域中,我们也可以使用一些方式来实现对字段位置的灵活控制:
struct TestBitField_s
{
uint8_t a : 1;
uint8_t b : 1;
uint8_t c : 1;
uint8_t d : 1;
uint8_t : 0; // 零长度位域可以终止上一个单元,让下面的字段从新的单元开始。
uint8_t : 1; // 如果单元中的某些位未被使用,可以不指定名称。
uint8_t e : 1;
uint8_t f : 1;
}; // 该结构体占2字节空间。
2、字节序
字节序(Endianness)是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
它分为:
-
大端序(Big endian):将高序字节存储在起始地址,此种字节序在存储数据的时候,数据的相对位置与读数时相同,使用时较为直观,例如0x11223344在内存中的存储方式为:
-
小端序(Little endian):将低序字节存储在起始地址,与大端相反,内存低地址存数据低位,内存高地址存数据高位,在调试时查看内存值的时候往往需要手动转字节序。
例如0x11223344在内存中的存储方式为:
其中我们常见的PC机、STM32、ARM均为小端系统,而网络字节序、C51则为大端模式。
在《C Primer Plus》这本书中对位域(位字段)的描述写到:
字段储存在一个int中的顺序取决于机器。
在有的机器上,存储顺序是从左到右,而在另一些机器上是从右到左。
最近因为在工作中使用了SPARC V8架构的处理器,该处理器为大端序,在移植曾经写过的代码时发现了位域的实现方式与小端序刚好相反,例如我们使用以下代码进行编程:
struct TestBitField_s
{
uint8_t a : 2;
uint8_t b : 2;
uint8_t c : 2;
uint8_t d : 2;
};
int main()
{
struct TestBitField_s testBitField = {0, 1, 2, 3};
printf("%x\n", *(uint8_t *)&testBitField);
}
在该大端系统中输出为1b,存储方式如下图:
而在小端系统中输出为e4,如下图:
由上述可知,大端序与小端序的位域存储方向是相反的。
在大端序下系统会按照定义顺序从高位向低位存储,小端反之。
可以看到,在大端序的数据存储方式总是按照最直观的方式进行的(从内存低地址到高地址,从最高比特位到最低比特位),而小端反之。
位域的这个特性使得其在可移植性方面表现较差,尤其是当你有几百个这种使用位域定义的量时,移植起来工作量就很大了,所以很多地方都会说应该尽量避免使用位域,不过在我们了解它的情况下,适当的使用确实可以产生事半功倍的效果。
END
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)