`

[C++] C/C++位结构(位域)[转]

    博客分类:
  • C++
 
阅读更多

转自:http://hi.baidu.com/liudeandg/blog/item/121633ddd2bf71265882dd20.html

 

位域 :   
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:     
struct 位域结构名     
{ 位域列表 };     
其中位域列表的形式为: 类型说明符 位域名:位域长度     
例如:     
struct bs     
{     
int a:8;     
int b:2;     
int c:6;     
};     
位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:     
struct bs     
{     
int a:8;     
int b:2;     
int c:6;     
}data;     
说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:     

1. 一个位域必须存储在同一个字节中,不能跨两个字节(这个限制好像仅限于char型)。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:     
struct bs     
{     
unsigned a:4     
unsigned :0 /*空域*/     
unsigned b:4 /*从下一单元开始存放*/     
unsigned c:4     
}     
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。     

2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。     

3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:     
struct k     
{     
int a:1     
int :2 /*该2位不能使用*/     
int b:3     
int c:2     
};     
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。     

二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名•位域名 位域允许用各种格式输出。     
main(){     
struct bs     
{     
unsigned a:1;     
unsigned b:3;     
unsigned c:4;     
} bit,*pbit;     
bit.a=1;     
bit.b=7; //注意:位域的赋值不能超过该域所能表示的最大值,如b只有3位,能表示的最大数为7,若赋为8,就会出错   
bit.c=15;

 

printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c=1;
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);
}
上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。 

程序的9、10、11三行分别给三个位域赋值。( 应注重赋值不能超过该位域的答应范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针 方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符"&=", 该行相当于: pbit->b=pbit->b&3位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为 3)。同样,程序第16行中使用了复合位运算"=", 相当于: pbit->c=pbit->c1其结果为15。程序第17行用指针方式输出了这三个域的值。 

我们再来看看下面两个结构体定义:
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)发现结果为8,也就是说编译器把a、b、c一起捆绑起来并以int做对齐了。就是说不够一个类型的size时,将按其中最大的那个类型对齐。此 处按int对齐。

 

 

 

网上有如下的解释:
/*************************************************************************
C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,
允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字
段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字
段将从新的存储单元开始,其偏移量为其类型大小的整数倍;(经测试此条好像仅限于char型,整型可以跨域存储)
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方
式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
***************************************************************************/

struct s1 

int i: 8; 
int j: 4; 
int a: 3; 
double b; 
}; 

struct s2 

int i: 8; 
int j: 4; 
double b; 
int a:3; 
}; 

printf("sizeof(s1)= %d\n", sizeof(s1)); 
printf("sizeof(s2)= %d\n", sizeof(s2)); 
result: 16, 24 
第一个结构体中,i,j,a共占15个位,不足8个字节,按double 8字节对齐,共16字节

 

第二个结构体中,i,j共占12位,不足8字节,按8字节对齐,a也按8字节对齐,加上double共8+8+8=24个字节

 

 

位域的定义如下 :

      有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: 
struct 位域结构名 
{ 位域列表 };
其中位域列表的形式为: 类型说明符 位域名:位域长度

位域有几点说明:

1、一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始,比如利用空域和无位域名的位域。

2、位域的长度不能大于数据类型本身的长度,比如int类型就能超过32位二进位。有其他人说是不能超过8位,我在我的机子上(linux as4系统)是可以实现int :32的位域长度的。莫非是位域重新进行定义了?

3、位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。

二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名

位域允许用各种格式输出。

举个例子:

       1 #include <stdio.h>
2
3 int main()
4 {
5     struct bs
6     {
7         char a:1;
8         char b:3;
9         char :0; /*空域*/
10         char c:4;
11         char :6; /*无位域名的位域*/
12         int e:18;
13     } __attribute__((packed)); /*必须增加__attribute__((packed)),否则对齐的时候会和预计结果不同*/
14
15     struct bs bit,*pbit;
16
17     printf("size of char is %d\n", sizeof(char));
18     printf("size of int is %d\n", sizeof(int));
19
20     printf("size of bit is %d\n", sizeof(bit));
21
22     return 0;
23 }

运行的结果1如下:

size of char is 1
size of int is 4
size of bit is 4

如果把程序第12行的e:18改为19的话,那么运行结果2如下:

size of char is 1
size of int is 4
size of bit is 5

如果把程序第13行的__attribute__((packed))去掉,那么运行结果3如下:

size of char is 1
size of int is 4
size of bit is 8

对运行结果123进行比较可以得知:

1、位域长度可以大于8,具体看数据类型,我测试了每一种数据类型,每一种类型的最大位域长度均为自身类型的长度;

2、空域的作用在添加了__attribute__((packed))之后失效;

3、未添加__attribute__((packed))的位域会对数据进行对齐:

 

例如:

struct bf 
{
  
char a:5;
  
int b:27;
}
;

 

bf的字节数为4,如果将a的位域改为6,那么a和b的位域长度和超过int的长度32,int类型根据自身长度的倍数进行对齐,所以bf的字节数则变为8。

同样

struct bf
{
    
int b:27;
    
char a:5;
}

也有同样的结论。

如果添加了了__attribute__((packed)),那么系统会对数据进行压缩,而不会发生bf的结果。

 

 

分享到:
评论

相关推荐

    c/c++类的位域详细介绍

    "C/C++位域详细介绍" C/C++中的位域(Bit Field)是指在结构体中使用的位域成员,它们可以将整数成员存储到比编译器通常允许的更小的内存空间中。位域的宽度可以通过常量整数表达式来指明。这种技术广泛应用于需要...

    C++ 位域

    总结来说,C++的位域是一种高级的数据结构,它允许程序员精确控制内存的使用,提高代码效率。然而,由于编译器实现的差异,位域在跨平台编程时可能存在兼容性问题,因此在使用位域时需谨慎,特别是在编写需要移植的...

    c c++位域研究总结!!!

    位域(Bit-fields)是 C 语言和 C++ 语言中都有的一个概念,但是位域有很多需要注意的问题。其中一个重要的问题是大端和小端字节序的问题。 大端和小端字节序是计算机科学中的一种约定,用于描述多字节数字在计算机...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    12.2 位域(位段) 194 12.3 本章小结 13 文件 13.1 C文件概述 197 13.2 文件指针 198 13.3 文件的打开与关闭 199 13.3.1 文件的打开(fopen 函数) 200 13.3.2 文件关闭函数(fclose函数) 202 13.4 文件的读写 204 ...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    12.2 位域(位段) 194 12.3 本章小结 13 文件 13.1 C文件概述 197 13.2 文件指针 198 13.3 文件的打开与关闭 199 13.3.1 文件的打开(fopen 函数) 200 13.3.2 文件关闭函数(fclose函数) 202 13.4 文件的读写 204 ...

    构造数据类型PPT学习教案.pptx

    位域是将一个字节的二进制位划分为多个独立的区域,每个区域有自己的位数。这在需要节省存储空间的情况下非常有用。定义位域结构如下: ```cpp struct Flags { unsigned isSet : 1; // 1位的位域,表示一个布尔状态...

    C++ 面试C++ 面试必看C++ 面试必看C++ 面试必看

    数组是 C++ 语言中的一种数据结构,用于存储一组相同类型的数据。字符串是 C++ 语言中的一种数据类型,用于存储字符序列。函数指针是 C++ 语言中的一种数据类型,用于存储函数的地址。类和对象是 C++ 语言中的一种...

    嵌入式C/C++语言精华文章集锦

    在C++中,`extern "C"`是一个关键字,用于告诉编译器按照C语言的规则来处理声明或定义,而不是C++的规则。这在使用C库函数或在C++代码中调用C代码时特别有用,因为它避免了C++的名称修饰(name mangling),确保了...

    嵌入式C/C++语言精华文章集锦

    ### 嵌入式C/C++语言精华文章集锦概览 #### 1. C/C++语言struct深层探索 在嵌入式系统编程中,`struct`是非常重要的数据组织工具,它允许程序员定义复杂的复合数据类型,从而更方便地管理和传递数据。在本章节中,...

    C/C++笔试题

    ### C/C++ 笔试题详解 #### 1. `volatile` 是什么? `volatile` 是一个关键字,在 C/C++ 中用来修饰变量。当一个变量被声明为 `volatile` 类型时,编译器不会对它进行优化(例如,将它保存在寄存器中而不是内存中...

    详细解读C++编程中的匿名类类型和位域

    位域是C++中一种节省存储空间的机制,允许结构体或类的成员占用比整型类型更少的位。位域声明的语法是`declarator : constant-expression`,其中`declarator`是成员的名称,`constant-expression`指定了成员占用的...

    C和C++嵌入式系统编程面试题 C和C++

    在嵌入式系统编程领域,C和C++是两种常用且强大的编程语言。它们的高效性、灵活性和底层控制能力使得它们成为构建嵌入式软件的理想选择。以下是一些关于C和C++嵌入式系统编程的重要知识点: 1. **内存管理**: - *...

    C++ 旅程

    位域(Bit Fields),也被称为位段,是C/C++中的一种特殊的数据结构,它允许在一个整数类型(通常是`unsigned int`)中定义一系列具有固定宽度的字段,每个字段占据一个或多个位。这种特性非常适用于硬件接口编程和...

    行业分类-电子-关于位域操作电路及位域操作方法的说明分析.rar

    在软件层面,高级编程语言如C和C++也提供了位字段(bit-field)结构,允许程序员定义结构体中的变量为固定宽度的位。 位域操作在电子政务领域也有广泛应用,比如在智能卡、身份认证和数据安全等方面。通过位域操作...

    位域探秘:C语言中的位域全解析

    2. **结构化语言**:C语言支持结构化编程,允许使用条件语句、循环、函数等结构化编程概念。 3. **内存管理**:C语言提供了对内存的直接控制,包括动态内存分配和手动内存管理。 4. **可移植性**:C语言编写的程序...

    C++与C#的一些比较

    此外,C#的`long`类型是64位,而C++中的`long`通常是32位。 C#的`struct`是值类型,与C++的`class`(引用类型)在语义上有本质区别。值类型在赋值或作为参数传递时会复制整个对象,而引用类型仅复制对对象的引用。 ...

    C++头文件和外设例程程序包安装

    位域结构方法利用C++的struct和bits-field特性,定义外设寄存器结构,简化对寄存器的操作。在使用Bits-Fields编程时,需要考虑Read-Modify-Write(RMW)问题和代码大小的影响。 4. **外设范例** 示例代码通常包括...

    C281x C/C++ Header Files and Peripheral Examples

    ### C281x C/C++ Header Files and Peripheral Examples #### 一、设备支持与介绍 本软件包支持包括但不限于以下几种281x系列器件:TMS320F2812、TMS320F2811、TMS320F2810、TMS320C2812、TMS320C2811、TMS320C...

Global site tag (gtag.js) - Google Analytics