结构体中使用的bit操作

毋庸置疑,位域的引入给用户的最大的好处莫过于可以有效的利用'昂贵'的内存和操作bit的能力了。而且这种操作bit位的能力很是方便,利用结构体域名即可对这些bit进行操作。例如:

struct foo { 
int a : 1; 
int b : 2; 
short c : 1; 
};

struct foo aFoo; 
aFoo.a = 1; 
aFoo.b = 3; 
aFoo.c = 0;

通过结构体实例.域名即可修改某些bit得值,这些都是编译器的'甜头'。当然我们也可以自己通过一些'掩码'和移位操作来修改这些bit,当然如果不是十分需要,我们是不需要这么做的。

位域还提供一种叫'匿名'位域的语法,它常用来'填缺补漏',由于是'匿名',所以你不能像上面那样去访问它。如: 
struct foo1 { 
int a : 1; 
int : 2; 
short c : 1; 
}; 
在foo1的成员a和c之间有一个2 bits的匿名位域。

在foo结构体的定义中,成员a虽然类型为int,但是它仅仅占据着4个字节中的一个bit的空间;类似b占据2个bit空间,但是b到底是占据第一个int的2个bit空间呢还是第二个int的2个bit空间呢?这里实际上也涉及到如何对齐带有'位域'的结构体这样一个问题。我们来分析一下。

我们再来看看下面两个结构体定义: 
struct foo2 { 
char a : 2; 
char b : 3; 
char c : 1; 
};

struct foo3 { 
char a : 2; 
char b : 3; 
char c : 7; 
}; 
我们来打印一下这两个结构体的大小,我们得到的结果是: 
sizeof(struct foo2) = 1 
sizeof(struct foo3) = 2 
显然都不是我们期望的,如果按照正常的内存对齐规则,这两个结构体大小均应该为3才对,那么问题出在哪了呢?首先通过这种现象我们可以肯定的是:带有'位域'的结构体并不是按照每个域对齐的,而是将一些位域成员'捆绑'在一起做对齐的。以foo2为例,这个结构体中所有的成员都是char型的,而且三个位域占用的总空间为6 bit < 8 bit(1 byte),这时编译器会将这三个成员'捆绑'在一起做对齐,并且以最小空间作代价,这就是为什么我们得到sizeof(struct foo2) = 1这样的结果的原因了。再看看foo3这个结构体,同foo2一样,三个成员类型也都是char型,但是三个成员位域所占空间之和为9 bit > 8 bit(1 byte),这里位域是不能跨越两个成员基本类型空间的,这时编译器将a和b两个成员'捆绑'按照char做对齐,而c单独拿出来以char类型做对齐,这样实际上在b和c之间出现了空隙,但这也是最节省空间的方法了。我们再看一种结构体定义:

struct foo4 { 
char a : 2; 
char b : 3; 
int c : 1; 
};

在foo4中虽然三个位域所占用空间之和为6 bit < 8 bit(1 byte),但是由于char和int的对齐系数是不同的,是不能捆绑在一起,那是不是a、b捆绑在一起按照char对齐,c单独按照int对齐呢?我们打印一下sizeof(struct foo4)发现结果为4,也就是说编译器把a、b、c一起捆绑起来并以int做对齐了。

通过上面的例子我们发现很难总结出很规律性的东西,但是带有'位域'的结构体的对齐有条原则可以遵循,那就是:"尽量减少结构体的占用空间"。当然显式的使用内存对齐的机会也并不多。^_^ 

### 关于结构体成员的概念与用法 #### 什么是结构体结构体是一种用户自定义的数据类型,允许将不同类型的数据组合在一起形成一个新的数据单元。它广泛应用于C/C++以及其他支持复合类型的编程语言中。 #### 结构体成员的定义方式 在C语言中,可以通过以下几种形式来定义结构体及其成员[^1]: ```c // 方法一:直接定义并声明变量 struct Point { int x; float y; } p; // 方法二:先定义结构体再创建实例 struct Rectangle { double length; double width; }; struct Rectangle rect; ``` 而在C++里,除了继承了C风格外还提供了更灵活的方式处理结构体,比如可以直接访问公有成员而无需额外设定存取权限(除非特别指定为private或protected),这使得操作更加简便直观[^2]。 #### 如何使用结构体成员? 一旦定义好了结构体之后就可以像下面这样去初始化或者修改它的各个字段值: ```cpp #include <iostream> using namespace std; int main() { struct Person { string name; unsigned short age; }; // 初始化对象 Person person = {"Alice", 30}; cout << "Name: " << person.name << ", Age:" << person.age << endl; // 修改成员属性 person.age +=1 ; return 0; } ``` 另外值得注意的是,在某些特定场合下我们还可以利用域技术来自由控制单个字节内部不同置所代表的信息意义, 这对于硬件驱动程序开发尤其有用[^4]: ```c struct PackedStruct{ unsigned int f1 :1 ; /* 占用1bit */ unsigned int f2 :1 ; /* 同样也是1bit */ unsigned int num :6 ;/* 此处则占用了剩余的6bits*/ }; ``` 以上例子展示了如何在一个无符号整型数范围内分配不同的比特给各个独立的小部分使用. #### 跨越到其他现代编程语言中的类比物 尽管这里讨论的重点放在了传统意义上的过程式编程环境下的实现细节上;但实际上很多面向对象的语言也保留着相似的功能模块——即所谓的“记录”或者是“元组”。例如Python里的`namedtuple`, Swift里面的`structs`, Java Bean等等都是如此设计出来的产物用来满足开发者们构建复杂业务逻辑的需求。 ### 总结 无论是哪种具体的语法糖衣包裹之下,核心理念始终围绕着把若干关联紧密却又彼此异质化的要素封装成易于管理的整体这一主题展开论述。理解好这些基础知识有助于进一步深入探索计算机科学领域内的更多奥秘。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值