项目中的使用
/********************************************************************** * 函数名称: DebugPrint * 功能描述: 打印信息的总入口函数 * 程序里用DBG_PRINTF来打印, 它就是DebugPrint * 在config.h里有这样的宏定义: #define DBG_PRINTF DebugPrint * 输入参数: 可变参数,用法和printf完全一样 * 输出参数: 无 * 返 回 值: 0 - 成功 * -1 - 失败 ***********************************************************************/ int DebugPrint(const char *pcFormat, ...) { char strTmpBuf[1000]; char *pcTmp; va_list tArg; int iNum; PT_DebugOpr ptTmp = g_ptDebugOprHead; int dbglevel = DEFAULT_DBGLEVEL; /* 可变参数的处理, 抄自glibc的printf函数 */ va_start (tArg, pcFormat); iNum = vsprintf (strTmpBuf, pcFormat, tArg); va_end (tArg); strTmpBuf[iNum] = '\0'; pcTmp = strTmpBuf; /* 根据打印级别决定是否打印 */ if ((strTmpBuf[0] == '<') && (strTmpBuf[2] == '>')) { dbglevel = strTmpBuf[1] - '0'; if (dbglevel >= 0 && dbglevel <= 9) { pcTmp = strTmpBuf + 3; } else { dbglevel = DEFAULT_DBGLEVEL; } } if (dbglevel > g_iDbgLevelLimit) { return -1; } /* 调用链表中所有isCanUse为1的结构体的DebugPrint函数 * 用来输出调试信息 */ while (ptTmp) { if (ptTmp->isCanUse) { ptTmp->DebugPrint(pcTmp); } ptTmp = ptTmp->ptNext; } return 0; }
代码分析
该代码段是一个调试打印函数DebugPrint的实现。下面对代码进行详细分析:
函数签名:int DebugPrint(const char *pcFormat, …)
返回类型:int,表示函数执行结果的状态,成功为0,失败为-1。
参数:const char *pcFormat,格式化字符串,用于指定输出的格式和内容。
可变参数:…,用于传递格式化字符串中的变量参数。
局部变量:
char strTmpBuf[1000]:临时缓冲区,用于存储格式化后的字符串。
char *pcTmp:临时指针,用于指向待输出的字符串。
va_list tArg:可变参数列表对象,用于存储和访问可变参数。
int iNum:格式化后的字符串长度。
PT_DebugOpr ptTmp:指向调试操作结构体的临时指针。
int dbglevel:调试级别,默认为DEFAULT_DBGLEVEL。
可变参数处理:
va_start(tArg, pcFormat):初始化可变参数列表,将tArg指向第一个可变参数。
iNum = vsprintf(strTmpBuf, pcFormat, tArg):使用格式化字符串和可变参数列表将数据格式化到临时缓冲区strTmpBuf中,并返回格式化后的字符串长度。
va_end(tArg):清理可变参数列表。
字符串处理:
strTmpBuf[iNum] = ‘\0’:将临时缓冲区的最后一个字符设置为字符串结束符。
pcTmp = strTmpBuf:将临时指针指向临时缓冲区的起始位置。
调试级别处理:
如果格式化后的字符串以开头(x是一个0到9之间的数字),则将dbglevel设置为x。
dbglevel = strTmpBuf[1] - ‘0’:将字符转换为数字。
如果转换结果在0到9之间,将pcTmp指向strTmpBuf中跳过调试级别标识的位置。
否则,将dbglevel重置为DEFAULT_DBGLEVEL。
如果调试级别大于设置的最大调试级别g_iDbgLevelLimit,直接返回。
遍历调试操作结构体链表:
while (ptTmp):循环遍历链表,直到遍历到最后一个节点(NULL)。
如果当前结构体的isCanUse标志为1,表示该结构体可用,则调用该结构体的DebugPrint函数打印调试信息,传入的参数为pcTmp。
ptTmp = ptTmp->ptNext:指向下一个调试操作结构体。
返回值:
函数执行成功返回0,表示调试信息已输出。
如果调试级别超过了最大调试级别,函数直接返回-1,表示未输出调试信息。
该函数的作用是根据给定的调试级别和格式化字符串,将调试信息输出到所有可用的调试操作结构体中。函数首先将格式化字符串处理成可读的字符串,并根据调试级别判断是否输出调试信息。然后,遍历调试操作结构体链表,将调试信息传递给每个可用的结构体进行输出。
实现自己的printf函数
va_start、vsprintf 和 va_end 是 C 语言中的三个宏,它们通常一起使用来处理可变参数函数。
- va_start
宏用于初始化一个指向参数列表的 va_list 对象,该对象可以被后续的 va_arg 宏用于逐个访问参数列表中的每个参数。va_start 宏需要两个参数:第一个参数是一个 va_list 对象,第二个参数是可变参数函数中的最后一个命名参数,用于确定参数列表的开始位置。 - vsprintf
函数是一个可变参数函数,它可以将格式化的字符串和参数列表中的值按照指定的格式写入到一个字符串缓冲区中。vsprintf 函数需要三个参数:第一个参数是指向目标字符串缓冲区的指针,第二个参数是格式化字符串,第三个参数是一个 va_list 对象,该对象包含了要写入到字符串缓冲区中的参数列表。 - va_end
宏用于清理 va_list 对象,并释放它所占用的资源。它需要一个参数,即要清理的 va_list 对象。
下面是一个简单的示例,它演示了如何使用这三个宏来处理可变参数函数:
#include <stdio.h> #include <stdarg.h> void my_printf(char *format, ...) { va_list args; va_start(args, format); vprintf(format, args); va_end(args); } int main() { my_printf("Hello, %s! The answer is %d\n", "world", 42); return 0; }
在上面的示例中,我们定义了一个名为 my_printf 的函数,它接受一个格式化字符串和可变数量的参数。在函数内部,我们首先使用 va_start 宏初始化一个 va_list 对象,然后使用 vprintf 函数和该对象来输出格式化字符串和参数列表中的值。最后,我们使用 va_end 宏清理 va_list 对象。在 main 函数中,我们调用了 my_printf 函数,并传递了一个格式化字符串和两个参数。