结构体中常见的冒号的用法是表示位域。
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有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. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
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;
bit.c=15;
printf("%d,%d,%d/n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c|=15;
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->c|1其结果为15。程序第17行用指针方式输出了这三个域的值。
===================================================================================
endian和little
endian但是很少有人知道它们的实质,因为只要你在网上一google,出来的都是那个经典的典故,不可否认,那个典故很重要,但是那也仅仅是个故事而已,计算机也仅仅是利用了这个故事的名字罢了,说到它们的实质还要看存储式体系计算机刚开始的时候,那个时候人们纷纷将数据和指令存入内存,如今我们很坦然的说出一个字节八位,一个int型的数据32位,可是那个时候人们在设计这一切的时候却没有这么坦然,他们甚至都不把八位想的这么特殊,现在想想为什么一个字节是八位,为什么八位显得那么重要,其实这里并没有什么必然的东西,因为计算机是以二进制为基础的,八位一字节在硬件上最简单,最高效,当时的cpu的数据总线最多也就有8位,而且8位最大可以表示255,正好表示完所有的拉丁字符,可是随着软件硬件的进步,而且实际上我们必须表示一个大于255的数字而不是字符,那么八位的数据量太小了,因此必须用超过一个字节的内存存储更大的数据类型,这就涉及到如何安排这些多字节数据的字节顺序了,因为逻辑上虽然一个int就是一个整数,但是人们根本不会管它在物理上怎么被载入内存,应该是怎么简单怎么高效就怎么安排,就好像逻辑上一个进程有四个G的虚存空间,但是物理上怎么安排实际分给这个进程的内存就是操作系统的事情了,逻辑上连续物理上不一定连续在字节续上也有所体现,于是怎么安排多字节的数据就是个问题,像虚拟内存那样彻底分开然后建立一套映射机制显得没有必要,那么就有了big
endian和little endian这样的排列方式,以字节为一个单位,然后安排这些字节的位置,不至于太分散但又不失灵活,这就是策略,于是big
endian和little
endian其实是根据不同的侧重点而最终采用的两种方式罢了,其实现在的计算机已经都是32位的了,8位的基本字节要改变了,但是即使改了也还要面对字节序的问题,毕竟数据类型的大小没有上限,对于big
endian来说,自然数据的高位在内存的低位,按照cpu发出的访问指令据总线导致内存访问的顺序是从低地址到高地址的,也就是先访问到最高的数据位,按照二进制数据的编码,最高位是符号位,也就是说big
endian的机器最先访问到符号位,这对于运算来说是很有优势的,现在看看little endian的情况,正好和big
endian相反,数据总线最先访问到自然数据的最低位,但是想要的到符号不得不先看看数据占几个字节找到最高位,然后才能判定,很麻烦,但是先访问低位也有好处,比如位访问比较有效,cpu访问一个数据的第n位就需要简单的从该数的开始处移动n/8个位置即可,仅仅一个移位,很简单,这个特性可以很方便的进行类型转换,软件上类型转换比较频繁的用little
endian比较有效,而不怎么需要类型转换的,数据类型比较固定的就用big endian比较好。
现在我们看一个重要的发现,就是CISC/RISC与big/little
endian的关系,我们知道RISC拥有大量的寄存器,所有的计算都在寄存器进行,只有在load或者store的时候才访问内存,而这两个操作仅仅是加载或者存储一个数字,数字的符号以及大小等信息越容易得到越好,现在看看CISC,由于类型转换涉及到计算,涉及到一些cpu指令,比如将一个32位的寄存器放到一个16位的临时数据中,而32位数据的得到涉及到很多寻址方式,很可能数据就是从内存得到的,因此CISC中为了最快的类型转换需要最快的位访问,这就是说,对于CISC的cpu,用little
endian比较好,而对于RISC的cpu,用big endian比较好。
计算机中的很多事情都没有绝对的为什么,很多都是因为历史原因,在当时的历史条件下必须那样,也许也不是必须那样,而是因为那样更简单,然后就确定了一些规则,计算机不仅仅是一门科学,它还是一个工业,工业就要兼顾经济利益,于是向下兼容就很重要了,其实很多概念都是为了兼容而遗留下来的,于是很多人就将它们当成教条,这很不应该。