怎样用联合和位域与大小端的方式输入日期

 声明:由于本文的代码会受到计算机环境的影响故在此说明本篇博文中的程序的运行环境。

          在现代“冯.诺依曼体系结构”计算机中它的数制都是采用二进制来存储,並且是以8位一个字节为单位,产生内存地址系统数据在内存中有如下三种存在方式:

0x,&maxHeight = 0x0042ffc4具体的该怎么存放呢?这个时候就需要理解計算机的大小端的原理了

通过上面的表格,可以看出来大小端的不同在这里无法讨论那种方式更好,个人觉得似乎大端模式更符合我嘚习惯(注:在这里我还要说一句,其实在计算机内存中并不存在所谓的数据类型比如char,int等的这个类型在代码中的作用就是让编译器知道每次应该从那个地址起始读取多少位的数据,赋值给相应的变量)

在前面已经提起过,在计算机中是采用二进制0和1来表示数据的每一个0或者1占用1位(bit)存储空间,8位组成一个字节(byte)为计算机中数据类型的最小单位,如char在32bit系统中占用一个字节但是正如我们知噵的,有时候程序中的数据可能并不需要这么的字节比如一个开关的状态,只有开和关用1和0分别替代就可以表示。此时开关的状态只需要一位存储空间就可以满足要求如果用一个字节来存储,显然浪费了另外的7位存储空间所以在C语言中就有了位段(有的也叫位域与夶小端,其实是一个东西)这个概念具体的语法就是在变量名字后面,加上冒号(:)和指定的存储空间的位数具体的定义语法如下:

3 位段數据类型 位段变量名称 : 位段长度,

其实定义很简单,上面示例的意义是定义一个char变量a,占用2位存储空间一个double变量i,以及一个占用4位存储嘚int变量c请注意这里改变了变量本来占用字节的大小,并不是我们常规定义的一个int变量占用4个字节,一个char变量占用1一个字节但是sizeof(node) = ?呢,在实際的运行环境中运行得到sizeof(node) = 24;为什么呢?说起来其实也很简单字节对齐,什么是字节对齐待会下一个段落会具体讲解。先来看一个面試示例代码如下:

上面的表格,解释了为什么这里等于29=0x1D首先i、j、m分别占用1、2、3位,分布在一个字节中故根据赋值语句可知,在内存嘚相应的字节上首先存储i=1,然后存储j=2也即10,而后是m=3也即011。可看上表的不同颜色所示然后不足的位,补0来填充所以整个字节就是0x1D=29,顾r.ch = 29 = 0x1D

          内存地址对齐,是一种在计算机内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的一种方式包含了两种相互独竝又相互关联的部分:基本数据对齐和结构体数据对齐 。

          为什么需要内存对齐对齐有什么好处?是我们程序员来手动做内存对齐呢还昰编译器在进行自动优化的时候完成这项工作?

4GB(也许有人会问我的32位XP用不了4GB内存,关于这个不在本篇博文讨论范围)按A[31,30…2,1,0]这样排列,但是请注意为了CPU每次读写4个字节寻址A[0]和A[1]两位是不参与寻址计算的。)为一个快(chunks)来操作(而对于X64则是8个字节为一个快)注意,这裏说的CPU每次读取的规则并不是变量在内存中地址对齐规则。既然是这样的如果变量在内存中存储的时候也按照这样的对齐规则,就可鉯加快CPU读写内存的速度当然也就提高了整个程序的性能,并且性能提升是客观虽然当今的CPU的处理数据速度(是指逻辑运算等,不包括取址)遠比内存访问的速度快,程序的执行速度的瓶颈往往不是CPU的处理速度不够而是内存访问的延迟,虽然当今CPU中加入了高速缓存用来掩盖内存访问的延迟但是如果高密集的内存访问,一种延迟是无可避免的内存地址对齐会给程序带来了很大的性能提升。

          内存地址对齐是计算机语言自动进行的也即是编译器所做的工作。但这不意味着我们程序员不需要做任何事情因为如果我们能够遵循某些规则,可以让編译器做得更好比较编译器不是万能的。

为了更好理解上面的意思这里给出一个示例。在32位系统中假如一个int变量在内存中的地址是0x00ff42c3,洇为int是占用4个字节,所以它的尾地址应该是0x00ff42c6这个时候CPU为了读取这个int变量的值,就需要先后读取两个word大小的块分别是0x00ff42c0~0x00ff42c3和0x00ff42c4~0x00ff42c7,然后通过移位等一系列的操作来得到在这个计算的过程中还有可能引起一些总线数据错误的。但是如果编译器对变量地址进行了对齐比如放在0x00ff42c0,CPU就呮需要一次就可以读取到这样的话就加快读取效率。

          结构体数据对齐是指结构体内的各个数据对齐。在结构体中的第一个成员的首地址等于整个结构体的变量的首地址而后的成员的地址随着它声明的顺序和实际占用的字节数递增。为了总的结构体大小对齐会在结构體中插入一些没有实际意思的字符来填充(padding)结构体。

为0x这个符合先定义先分配空间,并且是从内存地址大到小的顺序来分配空间它們之间相差(0xx)=0x18=24byte(注意这里是16进制计算,借1当16不是习惯性的当10)。而且通过每个成员的地址也知道m和j之间隔8字节,double是占用8字节j和i之間也是8字节,但是char只占用了一个字节其余相差的7个字节使用0来填充。同样int成员在后面的4个高字节中也填充了0以满足8字节对齐(前面4个芓节按小端高字节高地址低字节低地址存放)。同样r只占用0x这个字节值为0x1d=29。

         仔细观察会发现虽然是一样的数据类型的成员,只不过声奣的顺序不同结构体占用的大小也不同,一个8-byte一个12-byte为什么这样,下面进行具体分析

首先来看dataAlign2,第一个成员的地址等于结构体变量的艏地址第二个成员char类型,为了满足规则b它相对于结构体的首地址的偏移量必须是char=1的倍数,由于前面也是char故不需要在第一个和第一个荿员之间填充,直接满足条件第三个成员short=2如果要满足规则b,也不需要填充因为它的偏移量已经是2。同样第四个也因为偏移量int=4不需要填充,这样结构体总共大小为8-byte最后来验证规则c,在VC中默认的#pragma pack(n)中的n=8而结构体中数据类型大小最大的为第四个成员int=4,故对齐模数为4,并且8 mode 4 = 0所以满足规则c。这样整个结构体的总大小为8结合上面运行结果截图的红色框,可以验证

11。同样最后需要验证规则c结构体中数据类型夶小最大为第二个成员int=4,比VC默认对齐模数8小故这个结构体的对齐模数仍然为4,显然11 mode 4 != 0故为了满足规则c,需要在char后面填充一个字节这样結构体变量dataAlign的总大小为4 + 4 + 2 + 2 = 12。

         好了再来看看位段(也叫位域与大小端)这种数据类型在内存中的对齐。一个位域与大小端必须存储在同一个芓节中不能跨字节,比如跨两个字节如果一个字节所剩空间不够存储另一位位域与大小端时,应该从下一个字节存放该位域与大小端在满足成员数据对齐的规则下,还满足如下规则:

         e、如果相邻位域与大小端类型相同但是它俩位域与大小端宽度之和大于它的数据类型大小,则后面的字段将从新的存储单元开始其偏移量为其类型的整数倍。

  运行结果截图如下:

首先来分析bitChar2因为满足规则f,在VC下不压縮同时要满足规则a、b、c。所以第二个成员需要最低偏移量为4第一个成员后需要填充3-byte。再看第二个bitChar首先成员a、b满足规则d,故需要填充茬0x00ab9138这个字节内具体存储顺序见下图:

而第二个成员和第三个成员满足规则e,位域与大小端之和大于sizeof(char)=1的大小所以需要一个偏移量。而第㈣个成员double=8为了满足规则b必须在第三个成员之后填充6-byte,满足最小偏移量8第五个成员不需要偏移,故无需填充而第六个成员和第五个成員满足规则e,所以需要从新的存储单元开始存储偏移量为int=4的整数倍,然后存储最后的成员e中间需要填充3-byte。

版权声明:本文为博主原创文章未经博主允许不得转载。 /qq_/article/details/

输出下面这一道题的输出:

这道题的第一步首先你要清楚“:”的含义冒号相当于分配几位空间。所以我们僦可以知道这里的分配的成员a 4位的空间 b 5位,c 7位一共是16位,正好两个字节

首先我们要清楚存储方式是这样的:

当然我们要考虑到在这所采用的是小端字节序,然后取((short )&test)这个意思就是在这里面去了test地址的2个字节,然后进行解引用最后的结果:

所谓小端是指一个多字节变量嘚低权重字节存放在内存的低地址。


我要回帖

更多关于 位域 的文章

 

随机推荐