位域

位域是指信息在保存时,并不需要占用一个完整的字节,而只需要占几个或一个二进制位。为了节省空间,C语言提供了一种数据结构,叫“位域”或“位段”。

“位域“是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数,每个域有一个域名,允许在程序中按位域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

位域的使用和结构成员的使用相同,其一般形式为:位域 变量名.位域名 位域允许用各种格式输出。

1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性)。

struct bitmap
{unsigned a : 1;unsigned b : 3;unsigned c : 4;
}bit;

  sizeof(bitmap) == 4;(整个struct的大小为4,因为位域本质上是从一个数据类型分出来的,在我们的例子中数据类型就是unsigned,大小为4,并且位域也是满足C 的结构体内存对齐原则的,等下我们会说到)。

2. 当然了位域也可以有空域。

struct bitmap{unsigned a:4;unsigned :0; /*空域*/unsigned b:4; /*从下一单元开始存放*/unsigned c:4;}sizeof(bitmap) == 8;

3. 在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。这里我们可以看到空域的作用是填充数据类型的剩下的位置,有时候我们只是想调整一下内存分配,则我们可以使用无名位域:

 struct bitmap {unsigned a:1;unsigned :2;unsigned b:3;unsigned c:2;};sizeof(bitmap) == 4;
4. 如果一个位域的位的分配超过了该类型的位的总数,则从下一个单元开始继续分配,
这个很好理解:
 struct bitmap{unsigned a : 8;unsigned b : 30;unsigned c : 4;};sizeof(bitmap) == 12;

  注意这个位域的大小是12而不是8,说明如果超了大小是立马从下一个单元开始分配。

位域的使用主要出现在如下两种情况:
 (1)当机器可用内存空间较少而使用位域可以大量节省内存时。如,当把结构作为大数组的元素时。
 (2)当需要把一结构或联合映射成某预定的组织结构时。例如,当需要访问字节内的特定位时。
当要把某个成员说明成位域时,其类型只能是int,unsigned int与signed int三者之一(说明:int类型通常代表特定机器中整数的自然长度。short类型通常为16位,long类型通常为32位,int类型可以为16位或32位.各编译器可以根据硬件特性自主选择合适的类型长度.

关于位域还需要提醒读者注意如下几点:
其一,位域的长度不能大于int对象所占用的字位数.例如,若int对象占用16位,则如下位域说明是错误的:
     unsigned int x:17;
其二,由于位域的实现会因编译程序的不同而不同,在此使用位域会影响程序的可移植性,在不是非要使用位域不可时最好不要使用位域.
其三,尽管使用位域可以节省内存空间,但却增加了处理时间,在为当访问各个位域成员时需要把位域从它所在的字中分解出来或反过来把一值压缩存到位域所在的字位中.
其四,位域的位置不能访问,因些不能对位域使用地址运算符号&(而对非位域成员则可以使用该运算符).从而,即不能使用指向位域的旨针也不能使用位域的数组(因为数组实际上就是一种特殊的指针).另外,位域也不能作为函数返回的结果.
最后还要强调一遍:位域又叫位段(位字段),是一种特殊的结构成员或联合成员(即只能用在结构或联合中).

内存对齐:


1. 说到位域就不得说下内存对齐的东西,其实内存对齐也很简单,只是不同的编译器实现不一样,至于为什么要内存对齐,这个要从CPU的基本工作原理说起,但是首先要明白,无论我们是否内存对齐,CPU大多数情况都是能正常工作的(前提:对于大多数IA32指令都可以这么说,但是部分指令,如SSE多媒体指令这些就不行,这些指令有特殊内存对齐要求,比如16字节对齐,任何不满足内存对齐的地址访问储存器都是会导致异常,对于这些指令,编译器必须在编译的时候采取强制内存对齐)。

  实现内存对齐可以提高CPU的性能,比如处理器能一次取出8个字节,这个时候必须要求数据地址要8字节对齐,这个是和CPU和储存器的外围电路决定的,在内存对齐的情况下,CPU从储存器取出这8个字节只需要一个时钟周期,但是如果这个地址不是8字节对齐,那么CPU可能就需要两个时钟周期才能取出这8个字节。

  对于IA32,每个栈帧都惯例16字节对齐,编译器一般也会那么做,但是对于数据类型不同的编译器表现可能不一样,对于Windows(VC编译器),任何K字节的基本对象的地址都必须是K的倍数(比如对于int,必须4字节对齐,对于double,必须8字节对齐),这很大程度上提高了储存器和CPU的工作性能,但是对存储空间的浪费比较严重;对于Linux,惯例是8字节数对齐4字节边界(比如double可以4字节对齐)。对于Windows好Linux,数据类型long double都有4字节对其的要求,对于GCC,long double分配12字节(虽然它只占10字节大小)。

  所以我们有一般规则:

struct X
{char a;  float b;  int c;  double d;  unsigned e;
};
sizeof(X) == 32;

  内存对齐状况应该是下面这个样子:

struct X
{char a; // 1 bytes  char padding1[3]; // 3 bytes  float b; // 4 bytes  int c; // 4 bytes  char padding2[4]; // 4 bytes  double d; // 8 bytes  unsigned e; // 4 bytes  char padding3[4]; // 4 bytes};
sizeof(X) == 32;

  (其中最后的4个字节的填充是因为规则4,看下面)。

2. 如果自定义数据类型含有位域,则内存对齐满足以下原则:

  1. 如果相邻的位域的数据类型相同,则按照分配位的大小来,详情看我上面写的位域的第5个情况。

  2. 如果相邻的位域的数据类型不相同,则不同编译器实现不一样,有些编译器选择不压缩。

  3. 如果位域不连续,中间含非位域,则按标准数据类型大小划分,比如:

struct bitmap
{unsigned a : 2;  int b;  unsigned c : 3;
};
sizeof(bitmap) == 12;

3. 另外可以通过添加#pragma pack(n)来强制改变内存分配情况,比如在VC编译器中:


struct bitmap{unsigned a;double c;};sizeof(bitmap) == 16;

  加了#pragma pack(4),则强制内存对齐4字节,再测试下其大小:

#pragma pack(4)
struct bitmap
{unsigned a;  double c;
};
sizeof(bitmap) == 12;

  当然,如果#pragma pack(n)的n大于本身数据类型的宽度,则按数据类型的宽度来分配:

struct bitmap
{double c;  int k;  int m;
};
sizeof(bitmap) == 16 != 32

4. 自定义类型(C结构体,C++聚合类)的最后的内存对齐,是按照自定义类型内的最大类型的宽度来的,比如上面那个例子去掉int m:


struct bitmap{double c;int k;};
sizeof(bitmap) == 16

  必须以double进行8字节对齐(VC编译器)。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

C语言:--位域和内存对齐相关推荐

  1. C语言 结构体,内存对齐,对齐参数,偏移量

    文章目录 sizeof() 内存对齐 内存对齐的背景 全部规则汇总(看这里) 典型例子: Pragma Pack(n)与内存分配 #pragma pack对对齐模数的影响 偏移量 sizeof() 对 ...

  2. 字节对齐《c和指针》笔记--包含位域结构体的内存对齐(32bit,GCC)

    最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--字节对齐 C99划定int.unsigned   int和bool可以作为位域类型.但编译器几乎都对此作了扩展,答应其它类型类型的存在. ...

  3. C语言结构体的大小 — — 内存对齐和位域

    C语言结构体对齐 C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结 ...

  4. C++随记总结(1)----关于C++中的大小端、位段(惑位域)和内存对齐

    C++随记总结(1)----关于C++中的大小端.位段(惑位域)和内存对齐 声明:欢迎任何人和组织转载本blog中文章,但必须标记文章原始链接和作者信息. 本文链接:http://www.cnblog ...

  5. c语言20字节的内存的数据怎么读取_C++编程-内存对齐

    内存对齐可以大大提升内存访问速度,是一种用空间换时间的方法. 1.内存对齐的计算机原理 内存地址对齐,是一种在计算机内存中排列数据(表现为变量的地址).访问数据(表现为CPU读取数据)的一种方式,包含 ...

  6. C 语言编程 — 内存对齐

    目录 文章目录 目录 内存对齐 为什么要内存对齐? 内存对齐跟平台有关 对齐系数 使用 pragma 宏指令修改对齐系数 内存对齐的原则 内存对齐 计算机的内存空间都是按照字节划分的,元素(包括:变量 ...

  7. C语言内存编址和寻址、内存对齐

    内存编址和寻址.内存对齐 内存的编址方法 关键:内存编址是以字节为单位的 内存和数据类型的关系 内存对齐 内存的编址方法 在程序运行时,计算机中CPU实际只认识内存地址,而不关心这个地址所代表的空间在 ...

  8. 内存对齐分配策略(含位域模式)

    1:内存对齐定义:     现在使用的计算机中内存空间都是按照字节划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际上计算机系统对于基本数据类型在内存 中的存放位置都有限制,要求 ...

  9. C语言 | 内存对齐02 - 为什么会有内存对齐?它解决了什么问题

    文章目录 一.前言 二.内存对齐为4个字节的好处 三.内存对齐的目的是以空间换取速度 3.1.内存对齐为4的例子 3.2.内存没有使用内存对齐的例子 四.掌握内存对齐的必要性 一.前言 内存对齐的目的 ...

最新文章

  1. linux隐藏文件的方法,Linux下隐藏文件的操作方法
  2. JavaScript强化教程 -- cocosjs场景切换
  3. BZOJ2648: SJY摆棋子
  4. Kenshin Cui's Blog
  5. C++(STL):05---智能指针之unique_ptr
  6. aes离线解密工具_如何在Python中解密OpenSSL AES加密文件?
  7. [ECharts]echarts/config is not exists
  8. Docker入门与实践之 Dockerfile 语法详解
  9. AutoPtr 的局限性
  10. SQLServer中sp_Who、sp_Who2和sp_WhoIsActive介绍和查看监视运行
  11. 一款免费好用的英文润色软件(1Checker没错,这是免费的)
  12. 爬虫出现Forbidden by robots.txt
  13. 地理位置数据存储方案——Redis GEO
  14. 编辑状态打开mysql表,Mysql 数据库 基本操作以及调用(一)
  15. android之银行卡手机号码自动补齐空格
  16. 介绍几个常用的Chrome谷歌浏览器插件
  17. android系统文件重命名文件格式,安卓手机上文件的后缀名怎么改?
  18. 6、I.MX6ULL学习笔记—主频和时钟配置
  19. word无法创建工作文件,请检查临时环境变量
  20. 商务快车软件测试大乐,商务快车这个软件有用过的吗?效果怎么样? 爱问知识人...

热门文章

  1. Angular5 JWT身份验证(Spring Boot安全性)
  2. 如何使用Java和XML Config在Spring Security中启用HTTP基本身份验证
  3. Java Web应用程序的SecureLogin
  4. eclipse 重构_Eclipse对类固醇的重构
  5. 使用Gradle禁止Java和Spring Boot Web应用程序中的FindBugs警告
  6. java超出gc开销限制_超出了GC开销限制– Java堆分析
  7. 使用djcproxy创建代理对象
  8. js删除两个集合中共同元素_多个集合中的共同和独特元素
  9. 休眠:在sqlRestriction上使用联接表别名
  10. AssertJ的SoftAssertions –我们需要它们吗?