现在的位置: 首页 > 综合 > 正文

C语言学习笔记(14)

2012年09月23日 ⁄ 综合 ⁄ 共 1547字 ⁄ 字号 评论关闭

1. 位域

我们来看一个表示日期的结构体:

typedef struct
{
    unsigned int year;
    unsigned int month;
    unsigned int day;
}MyDate;

但是我们可以发现,其实year最大也不会超过四位数,month也就是12,而day最大也就是31。但是我们在上述的结构体中,却为其分配了4*3=12字节的内存,是不是很浪费呢?

C语言为了解决这个问题,提出了一个概念,叫位域。看段代码:

typedef struct
{
    unsigned int year:7;
    unsigned int month:4;
    unsigned int day:5;
}MyDate;

这个的意思是,我为结构体中的每一个字段分配指定的位数,比如,我为year分配了7位。这样就有效地节省了内存。

网上有着有限的几篇关于位域的文章,我觉得在CSDN的一个帖子中,对于关于位域分配内存的情况说的最为详细,原封不动地拿下来了:

C99规定int、unsigned   int和bool可以作为位域类型,但编译器几乎都对此作了扩展,

允许其它类型类型的存在。

使用位域的主要目的是压缩存储,其大致规则为:

1)   如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字

段将紧邻前一个字段存储,直到不能容纳为止;

2)   如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字

段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

3)   如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方

式,Dev-C++采取压缩方式;

4)   如果位域字段之间穿插着非位域字段,则不进行压缩;

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

2. 再谈联合体

在之前,我们谈到过使用联合体来节约内存空间,但是联合体也经常被用于一个其他的目的:从两个或更多的角度去看待内存块。

例如:

union int_Date
{
    int i;
    MyDate dt;
};

在上面的程序中,我们就可以用两种视角(整数视角和日期视角)来看待一个数据。

我们知道,在X86的机器中,我们常常会用到4个16位寄存器,这四个16位寄存器又可以分成8个8位的寄存器。例如,当我们改变ax的时候,其实al和ah也同时发生了改变,那么我们可以把这个关系用联合体来表示。

typedef union 
{
    struct 
    {
        short ax,bx,cx,dx;
    }word;
    struct
    {
        char al,ah,bl,bh,cl,ch,dl,dh;
    }byte;
}Regs;

int main (void)
{
    Regs regs;
    regs.byte.ah=0x12;
    regs.byte.al=0x34;
    printf("%x",regs.word.ax);
    return 0;
}

3. volatile限定符

volatile告诉编译器,这段内存空间所存储的值是易变的。volatile通常用于指向易变内存空间的指针。有个例子我觉得非常恰当。

我们假设*p指向的内存空间用于存放用户通过键盘所输入的字符,然后我们读取到这个字符,再将其放入到一个数组中。

但是一些优秀的编译器会发现,在这个过程中,p和*p都未被程序显式地改变,这样编译器就会对其作出优化,使*p只被取一次,这样就读入了数组中一些重复的数据,这明显不是我们想要的。

但是当我们在*p前加上volatile限定符,其实就是在告诉编译器,不要对该段程序进行优化,因为这段程序是易变的,每次的读取都要从内存中去重新取得。

在C#中也有volatile关键字,目的也是一样,volatile关键字代表某个变量可能会被多个并发的线程进行修改,所以通知编译器不要对其进行优化,这样就能保证每次取出来的值不是被缓存的,而是最新的值。

 

抱歉!评论已关闭.