c语言变量的作用域:C语言里的位域



C语言里位域是个比较复杂问题涉及方面也比较多有关位域基础内容可以参考以下文章:理解C语言位域

分析代码如下:

# "stdio.h"
# "memory.h"

struct BitSeg1{
a:4;
b:3;
};

struct BitSeg2{
char a:4;
char b:3;
};


{
struct BitSeg1 ba1;
ba1.a=1;
ba1.b=2;
prf("第次赋值后: a值为:%d\tb值为:%d\n",ba1.a,ba1.b);
ba1.a=100;
ba1.b=30;
prf("第 2次赋值后: a值为:%d\tb值为:%d\n",ba1.a,ba1.b);
char str="0123";
memcpy(&ba1,str,(BitSeg1));
prf("第 2次赋值后: a值为:%d\tb值为:%d\n",ba1.a,ba1.b);
prf("BitSeg1字节数为: %d\n",(BitSeg1));
prf("BitSeg2字节数为: %d\n",(BitSeg2));
0;
}
输出结果为:

次赋值后: a值为:1 b值为:2
  第 2次赋值后: a值为:4 b值为:-2
  第 2次赋值后: a值为:0 b值为:3
  BitSeg1字节数为: 4
  BitSeg2字节数为: 1



代码中BigSeg1定义了两个类型字段而且它们分别只占用4位和3位空间当BitSeg1中ab分别赋值为1和2时输出结果也如我们所料当第 2次赋值为100和30时输出结果却是4和-2为什么呢?

1.赋值问题

出现上述问题是由于赋值和位域效果共同形成a和b虽然都是类型但是在BigSeg1结构里它们只有4位和3位为实际有效位也就是BigSeg1中前4位是a接着3位是b(这里没有字节跨越问题)执行ba1.a=100语句其中100 2进制代码是:01100100只把这100 2进制数前面4位(已用红色字体表示)赋值给a那么ba1中a只是0100(b)结果当然是4咯然后是执行b1.b=30语句其中30 2进制代码为:00011110同样只把前3位(注意b定义有效位数是3位)赋值给b那么ba1中b就是110(b)结果是-2为什么?是这样我们定义b为类型也就是有符号整型如果想定义为无符号整型我们必须这样写unsigned 而有符号整型位是符号位用于表示正负(1表示负数0表示正数)那么对于b就会把b位(即1)做为符号位即b应该是负数而后面是它数值(即10(b))注意计算机里负数是按补码形式表示这种赋值下b确是110(它是补码按“即反加法则即十进制-2)结果就是-2了而刚才a给赋值为0100(b)时位是0解释为正数再举若使ba1.b=7那么ba1.b值是多少呢?7 2进制是0111前面3位直接给到b是负数读出来时按补码形式读那么就是-1了

总的句话:用位为理解位域

接下来是用memcpy对ba1进行内存copy就更应该用位来考虑位域了下面我们分析下:

首先(BitSeg1)值是4个字节先记住后面会对此问题进行详细解释

执行memcpy(&ba1,str,(BitSeg1))把str内容中前面4个字节内存里内容复制到ba1中我们先来看下str内存位信息(用16进制表示):

0x0012ff74:30  31  32 33

其中0x0012ff74时str地址起始位置30313233等16进制值分别表示'0''1''2''3'它们当然是ACII值啦

copy的后ba1内存位信息如下:

0x0012ff7c:30 31 32 33

ba1也是占4个字节空间所以不会出现内存溢出memcpy只是把相应内存复制到了ba1上位信息和str上信息

现在我们把30(H) 2进制写出来是:00110000ba1a占前面4位b占接下来3位直观地看a应该是0011(b)即十进制3b是000(b)即十进制0但看输出结果却是a=0b=3这又是为什么呢?其实很简单处理器定义字节前面4位是指该字节从右往左4位而不是从左往右4位所以a应该是0000(b)b应该是011(b)

2.字节对齐

回到上面留下字节数问题(BitSeg1)结果为4个字节按理来说BitSeg1有效位数是7位但为了快速运行个重要手段是减少内存读写次数所以处理器都是以字节倍数将内存中数据读到寄存器中所以把数据以字节形式对齐了就可以有效减少内存读写时间你可想想要处理器只读内存中7位是如何做个位?那倒不如次读8位

在做字节对齐时候也是有规则在32位系统里编译器会按类型进行字节对齐以它们位宽为基准在VC下:

char
  偏移量必须为(char)即1倍数


  偏移量必须为()即4倍数

float
  偏移量必须为(float)即4倍数

long
  偏移量必须为(long)即4倍数

double
  偏移量必须为(double)即8倍数


  偏移量必须为()即2倍数


BitSeg1里两个变量都是类型所以是4个字节对齐



而使用位域主要目是压缩存储减少内存占有量其大致规则为:
  1) 如果相邻位域字段类型相同且其位宽的和小于类型大小则后面字段将紧邻前个字段存储直到不能容纳为止;
  2) 如果相邻位域字段类型相同但其位宽的和大于类型大小则后面字段将从新存储单元开始其偏移量为其类型大小整数倍;
  3) 如果相邻位域字段类型区别则各编译器具体实现有差异VC6采取不压缩方式Dev-C采取压缩方式;
  4) 如果位域字段的间穿插着非位域字段则不进行压缩;
  5) 整个结构体总大小为最宽基本类型成员大小整数倍

  

再看个例子:

struct test1
{
char a:1;
char :2;
long b:3;
char c:2;
};

len = (test1);
对于上述例子len值应该是12解释如下:

首先以最长类型位宽做为偏移量最长是long型占4位所以区别类型的间应该是4个字节偏移即test1应该是4字节整数倍

char a:1; //用个字节去存储

char :2;  //空域和前面a类型相同而两个位域位宽相加仍然少于8位所以依然用1个字节表示

long b:3; //long类型位宽是4个字节和前面char类型区别所以b和a的间偏移4个字节它们的间自动补充3个字节

char c:2; //c和b又区别型以test1中最长long类型位宽进行偏移所以虽然char只用1个字节就够了

     //但依然要占4个字节

   总共是12字节
Tags:  c语言位运算符 c语言位运算 c语言变量作用域 c语言变量的作用域

延伸阅读

最新评论

发表评论