数就传递给fprintf ()。
?
? ? ?注意:可变参数宏不被ANSI/ISO
C++所正式支持。因此,应当检查编译器是否支持这项技术。?
?
? ? ?在标准C里,不能省略可变参数,但却可以给它传递一个空的参数,这会导致编译出错。因为宏展开后,里面的字符串后面会有个多余的逗号。为解决这个问题,GNU CPP中做了如下扩展定义:
?
? ? ? ? ? ? ? ? ? ? ? ?#define ?DBGMSG(format, ...) ?fprintf (stderr, format, ##__VA_ARGS__)
?
? ? ?若可变参数被忽略或为空,##操作将使编译器删除它前面多余的逗号(否则会编译出错)。若宏调用时提供了可变参数,编译器会把这些可变参数放到逗号的后面。
?
? ? ?同时,GCC还支持显式地命名变参为args,如同其它参数一样。如下格式的宏扩展:
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #define ?DBGMSG(format, args...) ?fprintf (stderr, format, ##args)
?
? ? ?这样写可读性更强,并且更容易进行描述。
?
? ? ?用GCC和C99的可变参数宏, 可以更方便地打印调试信息,如:
?
1 #ifdef DEBUG
2 ? ? #define DBGPRINT(format, args...) \
3 ? ? ? ? fprintf(stderr, format, ##args)
4 #else
5 ? ? #define DBGPRINT(format, args...)
6 #endif
? ? ?这样定义之后,代码中就可以用dbgprint了,例如dbgprint ("aaa [%s]", __FILE__)。
?
? ? ?结合第4节的“条件编译”功能,可以构造出如下调试打印宏:
?
复制代码
?1 #ifdef LOG_TEST_DEBUG
?2 ? ? /* OMCI调试日志宏 */
?3 ? ? //以10进制格式日志整型变量
?4 ? ? #define PRINT_DEC(x) ? ? ? ? ?printf(#x" = %d\n", x)
?5 ? ? #define PRINT_DEC2(x,y) ? ? ? printf(#x" = %d\n", y)
?6 ? ? //以16进制格式日志整型变量
?7 ? ? #define PRINT_HEX(x) ? ? ? ? ?printf(#x" = 0x%-X\n", x)
?8 ? ? #define PRINT_HEX2(x,y) ? ? ? printf(#x" = 0x%-X\n", y)
?9 ? ? //以字符串格式日志字符串变量
10 ? ? #define PRINT_STR(x) ? ? ? ? ?printf(#x" = %s\n", x)
11 ? ? #define PRINT_STR2(x,y) ? ? ? printf(#x" = %s\n", y)
12?
13 ? ? //日志提示信息
14 ? ? #define PROMPT(info) ? ? ? ? ?printf("%s\n", info)
15?
16 ? ? //调试定位信息打印宏
17 ? ? #define ?TP ? ? ? ? ? ? ? ? ? printf("%-4u - [%s<%s>]\n", __LINE__, __FILE__, __FUNCTION__);
18?
19 ? ? //调试跟踪宏,在待日志信息前附加日志文件名、行数、函数名等信息
20 ? ? #define TRACE(fmt, args...)\
21 ? ? do{\
22 ? ? ? ? printf("[%s(%d)<%s>]", __FILE__, __LINE__, __FUNCTION__);\
23 ? ? ? ? printf((fmt), ##args);\
24 ? ? }while(0)
25 #else
26 ? ? #define PRINT_DEC(x)
27 ? ? #define PRINT_DEC2(x,y)
28?
29 ? ? #define PRINT_HEX(x)
30 ? ? #define PRINT_HEX2(x,y)
31?
32 ? ? #define PRINT_STR(x)
33 ? ? #define PRINT_STR2(x,y)
34?
35 ? ? #define PROMPT(info)
36?
37 ? ? #define ?TP
38?
39 ? ? #define TRACE(fmt, args...)
40 #endif
复制代码
?
?
?
?
三 ?文件包含
? ? ?文件包含命令行的一般形式为:
?
#include"文件名"
?
? ? ?通常,该文件是后缀名为"h"或"hpp"的头文件。文件包含命令把指定头文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。
?
? ? ?在程序设计中,文件包含是很有用的。一个大程序可以分为多个模块,由多个程序员分别
编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。
?
? ? ?对文件包含命令要说明以下几点:
?
包含命令中的文件名可用双引号括起来,也可用尖括号括起来,如#include "common.h"和#include
。但这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的include目录),而不在当前源文件目录去查找;使用双引号则表示首先在当前源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。
一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。
文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。
?
?
?
?
四 ?条件编译
? ? ?一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
?
? ? ?条件编译功能可按不同的条件去编译不同的程序部分,从而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。
?
? ? ?条件编译有三种形式,下面分别介绍。
?
4.1 #ifdef形式
#ifdef ?标识符 ?(或#if defined标识符)
?
? ? 程序段1
?
#else
?
? ? 程序段2
?
#endif
?
? ? ?如果标识符已被#define命令定义过,则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),#else可以没有,即可以写为:
?
#ifdef ?标识符 ?(或#if defined标识符)
?
? ? 程序段
?
#endif
?
? ? ?这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可