C/C++ 位域知识小结

C/C++ 位域知识小结

几篇较全面的位域相关的文章:

http://www.uplook.cn/blog/9/93362/

C/C++位域(Bit-fields)之我见

C中的位域与大小端问题

内存对齐全攻略–涉及位域的内存对齐原则

本文主要对位域相关知识进行了一下梳理,参考如下:

C语言中的位域

史上最全的C位域总结2

C结构体之位域(位段)

C/C++中以一定区域内的位(bit)为单位来表示的数据成为位域,位域必须指明具体的数目。

位域的作用主要是节省内存资源,使数据结构更紧凑。

1. 一个位域必须存储在同一个字节中,不能跨两个字节,故位域的长度不能大于一个字节的长度。

如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

   struct BitField
   {
      unsigned int a:4;  //占用4个二进制位;
      unsigned int  :0;  //空位域,自动置0;
      unsigned int b:4;  //占用4个二进制位,从下一个存储单元开始存放;
      unsigned int c:4;  //占用4个二进制位;
      unsigned int d:5;  //占用5个二进制位,剩余的4个bit不够存储4个bit的数据,从下一个存储单元开始存放;
      unsigned int  :0;  //空位域,自动置0;
      unsigned int e:4;  //占用4个二进制位,从这个存储单元开始存放;
   };

2. 取地址操作符&不能应用在位域字段上;

3. 位域字段不能是类的静态成员;

4. 位域字段在内存中的位置是按照从低位向高位的顺序放置的;

struct BitField
  {
    unsigned char a:2;  //最低位;
    unsigned char b:3;
    unsigned char c:3;  //最高位;
  };
  union Union
  {
    struct BitField bf;
    unsigned int n;
  };
  union Union ubf;
  ubf.n = 0;    //初始化;
  ubf.bf.a = 0; //二进制为: 000
  ubf.bf.b = 0; //二进制为: 000
  ubf.bf.c = 1; //二进制为: 001
  printf("ubf.bf.n = %u\n", ubf.n);

位域中的位域字段按照从低位向高位顺序方式的顺序来看,那么,a、b、c这三个位域字段在内存中的放置情况是:

最高位是c:001,中间位是b:000,最低位是a:000;所以,这个位域结构中的8二进制内容就是: 00100000,总共8个位,其十进制格式就是32;

实际上打印出来的ubf.n值就是32;

ubf.n = 100; //二进制为: 01100100

printf("ubf.bf.a = %d, ubf.bf.b = %d, ubf.bf.c = %d\n", ubf.bf.a, ubf.bf.b, ubf.bf.c);

此时,对于位域ubf.bf来说,其位于字段仍然按照从低位向高位顺序方式的顺序放置,则,最高位是c:011,中间位是b:001,最低位是a:00;

所以,ubf.bf.a = 0; ubf.bf.b = 1; ubf.bf.c = 3;

实际上打印出来的结果也的确如此;不够存储下一个位域的4位,故设为空位域,不使用,自动置0;e从第四个字节处开始存放,占用4位;

5. 位域的对齐

1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

3.如果相邻的两个位域字段的类型不同,则各个编译器的具体实现有差异,VC6采取不压缩方式,GCC和Dev-C++都采用压缩方式;

4. 整个结构体的总大小为最宽基本类型成员大小的整数倍。

5. 如果位域字段之间穿插着非位域字段,则不进行压缩;(不针对所有的编译器)

  struct BFA
  {
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:3;
  };
  struct BFB
  {
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:3;
    unsigned int  d:4;  //多出来这个位域字段;
  };

sizeof(BFA)=1, sizeof(BFB)=8;

这也说明了第三点中"相邻两个位于字段类型不相同时,VC6采取不压缩的方式"

6. 当要把某个成员说明成位域时,其类型只能是int,unsigned int与signed int三者之一(说明:int类型通常代表特定机器中整数的自然长度。short类型通常为16位,long类型通常为32位,int类型可以为16位或32位.各编译器可以根据硬件特性自主选择合适的类型长度.见The C Programming Language中文 P32)。

尽管使用位域可以节省内存空间,但却增加了处理时间,在为当访问各个位域成员时需要把位域从它所在的字中分解出来或反过来把一值压缩存到位域所在的字位中.

#include <iostream>
 #include <memory.h>
 using namespace std;
 struct A
 {
     int a:5;
     int b:3;
 };
 int main(void)
 {
     char str[100] = "0134324324afsadfsdlfjlsdjfl";
         struct A d;
     memcpy(&d, str, sizeof(A));
     cout << d.a << endl;
     cout << d.b << endl;
     return 0;
 }

在32位x86机器上输出:

高位 00110100 00110011   00110001    00110000 低位
       ‘4‘       ‘3‘       ‘1‘          ‘0‘
其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001

解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的元素,那么就以处理器的位数为对齐单元。由于是32位处理器,而且结构体中a和b元素类型均为int(也是4个字节),所以结构体的A占用内存为4个字节。

上例程序中定义了位域结构A,两个个位域为a(占用5位),b(占用3位),所以a和b总共占用了结构A一个字节(低位的一个字节)。

当程序运行到14行时,d内存分配情况:

 高位 00110100 00110011   00110001    00110000 低位       ‘4‘       ‘3‘       ‘1‘          ‘0‘   其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001

d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)

d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)

另一个例子,来自http://blog.chinaunix.net/uid-28697486-id-3511598.htm

#include "stdio.h"

void main(int argn ,char *argv)
{
    struct     test {
        unsigned a:10;
        unsigned b:10;
        unsigned c:6;
        unsigned :2;//this two bytes can‘t use
        unsigned d:4;
        }data,*pData;
    data.a=0x177;
    data.b=0x111;
    data.c=0x7;
    data.d=0x8;

    pData=&data;
    printf("data.a=%x data.b= %x data.c=%x data.d=%xn",pData->a,pData->b,pData->c,pData->d);//位域可以使用指针

    printf("sizeof(data)=%dn",sizeof(data));   //4 bytes ,最常用的情况

    struct testLen{
    char a:5;
    char b:5;
    char c:5;
    char d:5;
    char e:5;
    }len;

    printf("sizeof(len)=%dn",sizeof(len));     //5bytes 规则2

    struct testLen1{
        char a:5;
        char b:2;
        char d:3;
        char c:2;
        char e:7;
        }len1;
    printf("sizeof(len1) =%dn",sizeof(len1));    //3bytes 规则1

    struct testLen2{
        char a:2;
        char :3;
        char b:7;
        long d:20; //4bytes
        char e:4;
        }len2;
    printf("sizeof(len2)=%dn",sizeof(len2));  //12 规则3,4,5,总长为4的整数倍,2+3 占1byte,b占1bye 由于与long对其,2+3+7 占4字节,后面 d 与 e进行了优化 占一个4字节

    struct testLen3{
        char a:2;
        char :3;
        char b:7;
        long d:30;
        char e:4;
        }len3;
    printf("sizeof(len3)=%dn",sizeof(len3));//12 规则3,4,5,总长为4的整数倍,2+3 占1byte,b占1bye 由于与long对其,2+3+7 占4字节,后面 d占一个4字节,为了保证与long对其e独占一个4字节
时间: 2024-10-12 15:49:39

C/C++ 位域知识小结的相关文章

awk基础知识小结(1)

1.使用规则 awk 适合于文本处理和报表生成,它还有许多精心设计的特性,允许进行需要特殊技巧程序设计. awk  的语法较为常见.它借鉴了某些语言的一些精华部分,如C 语言.python 和 bash. 第一个 awk 让我们继续,开始使用 awk,以了解其工作原理.在命令行中输入以下命令: $ awk '{ print }'  /etc/passwd 您将会见到 /etc/passwd 文件的内容出现在眼前.现在,解释 awk 做了些什么.调用 awk 时,我们指定  /etc/passwd

awk基础知识小结(2)

10.循环结构循环结构awk 的 while 循环结构,它等同于相应的 C 语言 while 循环.awk 还有 "do...while" 循环,它在代码块结尾处对条件求值,而不象标准 while 循环那样在开始处求值.它类似于其它语言中的 "repeat...until" 循环.示例:do...while 示例 {     count=1     do {    print "I get printed at least once no matter w

suid知识小结

1.1.1 suid知识小结:针对命令和二进制程序 1)用户或属主对应的前三位权限的x位上如果有s就表示suid权限.当x位上没有小写x执行权限的时候,suid的权限显示的就是大S. 2)suid作用是让普通用户可以以root(或其他)的用户角色运行只有root(或其他)账号才能运行的程序或命令,或程序命令对应本来没有权限操作的文件等.(注意和su及sudo的区别),suid为某个命令设置特殊权限(使用者为所有人).通过给rm命令设置suid加深学生对suid的理解. 3)问题:希望oldboy

javascript之正则表达式基础知识小结

javascript之正则表达式基础知识小结,对于学习正则表达式的朋友是个不错的基础入门资料. 元字符 ^ $ . * + ? = ! : | \ / ( ) [ ] { } 在使用这些符号时需要用“\”来进行转移. 如果记不清楚那些标点符号需要转移,可以在使用标点符号时都使用反斜杠“\” 简单匹配 1.直接量 /javascript/ 匹配带有“javascript”的字符串 比如“javascript is an object-oriented scripting language” 2.[

Android app开发知识小结

Android知识小结 这是一个知识的总结,所以没有详解的讲解. 一.分辨率Android中dp长度.sp字体使用.px像素.in英寸.pt英寸1/72.mm毫米 了解dp首先要知道density,density是一个抽象的单位,density值表示每英寸有多少个显示点,与分辨率是两个概念QVGA屏density=120         QVGA(240*320)HVGA屏density=160         HVGA(320*480)WVGA屏density=240        WVGA(

关闭selinux知识小结

今天有人问我,哎,福星,关闭selinux这块我还有点蒙,能给我分析分析么? selinux是什么? 1.     我的简单理解:selinux是一套安全的机制,在学习环境下建议关闭,功能方面可以一些机制所替代.比如防火墙等系列安全措施 selinux一定要关闭么? 1.     没有基础又不愿意倘坑的小伙伴可以关闭,工作中环境大都不开启 2.     开启的坏处是使用中需要倘很多的坑,如果有面对困难的勇气,那就别管它 修改selinux运行状态的相关 setenforce  1 | 0    

Linq知识小结

Linq语法小结:有两种形式的语法可供我们在写Linq查询时使用,分别是“查询语法”.“方法语法”.1)先看个列子,有个直观认识     int[] arr = { 12, 2,45,34,23,18,4};     //查询语法形式(推荐使用这种).返回的是一个可枚举类型     IEnumerable<int> results = from temp in arr                                where temp > 20              

180531-Spring中JavaConfig知识小结

原文链接:Spring中JavaConfig知识小结/ Sring中JavaConfig使用姿势 去掉xml的配置方式,改成用Java来配置,最常见的就是将xml中的 bean定义, scanner包扫描,属性文件的配置信息读取等 I. 几个基本注解 1. Configuration注解 在javaConfig中注解@Configuration用来代替一个xml文件,可以简单的理解他们的作用是相等的,一般bean的定义也都是放在被这个注解修饰的类中 如一个基本的配置文件如下 @Configura

C++ STL 知识小结

qwq...接近联赛,就在这里对STL做一点知识小结吧,因为STL曾经失分很多. 简介 (来自Baidu) STL是Standard Template Library的简称,中文名标准模板库,惠普实验室开发的一系列软件的统称.它是由Alexander Stepanov.Meng Lee和David R Musser在惠普实验室工作时所开发出来的.从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合.这里的“容器”和算法