内联、联合体及Voliate

上传人:飞*** 文档编号:5374785 上传时间:2017-08-30 格式:DOC 页数:12 大小:61.50KB
返回 下载 相关 举报
内联、联合体及Voliate_第1页
第1页 / 共12页
内联、联合体及Voliate_第2页
第2页 / 共12页
内联、联合体及Voliate_第3页
第3页 / 共12页
内联、联合体及Voliate_第4页
第4页 / 共12页
内联、联合体及Voliate_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《内联、联合体及Voliate》由会员分享,可在线阅读,更多相关《内联、联合体及Voliate(12页珍藏版)》请在金锄头文库上搜索。

1、inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代 C 中表达式形式的宏定义。 表达式形式的宏定义一例: #define ExpressionName(Var1,Var2) (Var1+Var2)*(Var1-Var2) 为什么要取代这种形式呢,且听我道来: 1 首先谈一下在 C 中使用这种形式宏定义的原因, C 语言是一个效率很高的语言,这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等一系列的操作,因此,效率很高,这是它在 C 中被使用的一个主要原因。 2 这种宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简

2、单替换,因此它不能进行参数有效性的检测,也就不能享受 C+编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。 3 在 C+中引入了类及类的访问控制,这样,如果一个操作或者说一个表达式涉及到类的保护成员或私有成员,你就不可能使用这种宏定义来实现(因为无法将 this 指针放在合适的位置)。 4 inline 推出的目的,也正是为了取代这种表达式形式的宏定义,它消除了它的缺点,同时又很好地继承了它的优点。 为什么 inline 能很好地取代表达式形式的预定义呢? 对应于上面的 1-3 点,阐述如下: 1 inline 定义的类

3、的内联函数,函数的代码被放入符号表中,在使用时直接进行替换, (像宏一样展开) ,没有了调用的开销,效率也很高。 2 很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。 3 inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。 在何时使用 inline 函数: 首先,你可以使用 inline 函数完全取代表达式形式的宏定义。 另外要注意,内联函数一般只会用在函数内容非常简单的时候,这是因为,内联函数的代码会在任何调

4、用它的地方展开,如果函数太复杂,代码膨胀带来的恶果很可能会大于效率的提高带来的益处。从两道经典试题谈 C/C+中联合体(union)的使用试题一:编写一段程序判断系统中的 CPU 是 Little endian 还是 Big endian 模式?分析:作为一个计算机相关专业的人,我们应该在计算机组成中都学习过什么叫 Little endian 和 Big endian。Little endian 和 Big endian 是 CPU 存放数据的两种不同顺序。对于整型、长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节);而

5、 Little endian 则相反,它认为第一个字节是最低位字节(按照从低地址到高地址的顺序存放数据的低位字节到高位字节)。例如,假设从内存地址 0x0000 开始有以下数据:0x0000 0x0001 0x0002 0x00030x12 0x34 0xab 0xcd如果我们去读取一个地址为 0x0000 的四个字节变量,若字节序为 big-endian,则读出结果为0x1234abcd;若字节序位 little-endian,则读出结果为 0xcdab3412。如果我们将 0x1234abcd 写入到以 0x0000 开始的内存中,则 Little endian 和 Big endian

6、模式的存放结果如下:地址 0x0000 0x0001 0x0002 0x0003big-endian 0x12 0x34 0xab 0xcdlittle-endian 0xcd 0xab 0x34 0x12一般来说,x86 系列 CPU 都是 little-endian 的字节序,PowerPC 通常是 Big endian,还有的CPU 能通过跳线来设置 CPU 工作于 Little endian 还是 Big endian 模式。解答:显然,解答这个问题的方法只能是将一个字节(CHAR/BYTE 类型)的数据和一个整型数据存放于同样的内存开始地址,通过读取整型数据,分析 CHAR/BYTE

7、 数据在整型数据的高位还是低位来判断CPU 工作于 Little endian 还是 Big endian 模式。得出如下的答案:typedef unsigned char BYTE; int main(int argc, char* argv)unsigned int num,*p;p = #num = 0;*(BYTE *)p = 0xff;if(num = 0xff)printf(The endian of cpu is littlen);else /num = 0xff000000 printf(The endian of cpu is bign);return 0;除了上述方

8、法(通过指针类型强制转换并对整型数据首字节赋值,判断该赋值赋给了高位还是低位 )外,还有没有更好的办法呢?我们知道,union 的成员本身就被存放在相同的内存空间(共享内存,正是 union 发挥作用、做贡献的去处),因此,我们可以将一个 CHAR/BYTE 数据和一个整型数据同时作为一个 union 的成员,得出如下答案:int checkCPU()union w int a;char b; c;c.a = 1;return (c.b = 1);实现同样的功能,我们来看看 Linux 操作系统中相关的源代码是怎么做的:static union char c4; unsigned long l

9、; endian_test = l, ?, ?, b ;#define ENDIANNESS (char)endian_test.l)Linux 的内核作者们仅仅用一个 union 变量和一个简单的宏定义就实现了一大段代码同样的功能!由以上一段代码我们可以深刻领会到 Linux 源代码的精妙之处!试题二:假设网络节点 A 和网络节点 B 中的通信协议涉及四类报文,报文格式为 “报文类型字段+报文内容的结构体” ,四个报文内容的结构体类型分别为 STRUCTTYPE1 STRUCTTYPE4,请编写程序以最简单的方式组织一个统一的报文数据结构。分析:报文的格式为“报文类型+ 报文内容的结构体”,

10、在真实的通信中,每次只能发四类报文中的一种,我们可以将四类报文的结构体组织为一个 union(共享一段内存,但每次有效的只是一种),然后和报文类型字段统一组织成一个报文数据结构。解答:根据上述分析,我们很自然地得出如下答案:typedef unsigned char BYTE; /报文内容联合体typedef union tagPacketContentSTRUCTTYPE1 pkt1;STRUCTTYPE2 pkt2;STRUCTTYPE3 pkt1;STRUCTTYPE4 pkt2;PacketContent; /统一的报文数据结构typedef struct tagPacketBYTE

11、pktType;PacketContent pktContent;Packet;总结在 C/C+程序的编写中,当多个基本数据类型或复合数据结构要占用同一片内存时,我们要使用联合体(试题一是这样的例证);当多种类型,多个对象,多个事物只取其一时(我们姑且通俗地称其为“n 选 1”),我们也可以使用联合体来发挥其长处(试题二是这样的例证)。位域有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,语言又提供了一种数据结构,称为“位域” 或“位段”。所谓“位域”是把一个字节中

12、的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: struct 位域结构名 位域列表 ;其中位域列表的形式为: 类型说明符 位域名:位域长度 例如: struct bsint a:8;int b:2;int c:6;位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: struct bsint a:8;int b:2;int c:6;data;说明 data 为 bs 变

13、量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。对于位域的定义尚有以下几点说明:1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bsunsigned a:4unsigned :0 /*空域*/unsigned b:4 /*从下一单元开始存放*/unsigned c:4在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用, b 从第二字节开始,占用 4 位,c 占用 4 位。2. 由于位域不允许跨两个字节,因此位

14、域的长度不能大于一个字节的长度,也就是说不能超过 8 位二进位。3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct kint a:1int :2 /*该 2 位不能使用*/int b:3int c:2;从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名位域名 位域允许用各种格式输出。main()struct bsunsigned a:1;unsigned b:3;unsigned c:4; bit,*pbit;bit.a=1;bit.b=7;bi

15、t.c=15;printf(%d,%d,%dn,bit.a,bit.b,bit.c);pbit=&bit;pbit-a=0;pbit-b&=3;pbit-c|=1;printf(%d,%d,%dn,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 行用指针方式输出了这三个域的值。C 语言关键字 volatile 的使用一个定义为 volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 研究报告 > 综合/其它

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号