一、可变参数函数的理解
二、可变参数函数的应用
1、可变参数的常规打印
直接参考代码,注释写的很清楚了。
代码的核心也是通过vprintf打印到控制台。
#include
#include
// 可变参数的头文件
#include
// 默认为打印到控制台
void script_log_error(char *fmt, ...)
{
// 定义可变参数列表
va_list ap;
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
// 打印可变参数
printf("数据为:");
vprintf(fmt, ap);
// 结束
va_end(ap);
printf("\n");
}
int main()
{
// 使用可变参数的函数打印
script_log_error("type is_3:%f,%s,%d hunhe5", 362.01, "123654", 50);
script_log_error("type is_5: no data");
}
2、可变参数的解析并打印
解析主要分四步处理:
1.通过循环遍历的方式,解析每个输入字符
2.把%d、%f、%s作为关键字解析,并获取对应的数据。
3.把修饰性字符与实际数据都转换成字符串并存储于二维数组中。
4.把二维数组中的内容转换到一维数组中,然后打印一维数组。
#include
#include
// 可变参数的头文件
#include
void script_dzlog_error(char *fmt, ...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
// 存储修饰性字符
char data_val[256] = {0};
// 存储每一段数据
char data[10][256] = {0};
int idx_d1 = 0;
int cnt_v = 0;
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
for (p = fmt; *p; p++) {
if(*p != '%') {
// 获取修饰性字符
data_val[cnt_v++] = *p;
continue;
}
switch(*++p) {
case 'd':
{
ival = va_arg(ap, int);
char tmp[128] = {0};
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
my_itoa(ival, tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
case 'f':
{
dval = va_arg(ap, double);
char tmp[128] = {0};
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
my_ftoa(dval, tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
case 's':
{
char tmp[128] = {0};
char tmp_cnt = 0;
for (sval = va_arg(ap, char *); *sval; sval++)
{
// printf("%c", *sval);
tmp[tmp_cnt++] = *sval;
}
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
// printf("tmp:%s\n", tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
default:
data_val[cnt_v] = *p;
// putchar(*p);
break;
}
}
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// printf("data_tail:%s\n", data_val);
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
va_end(ap);
// 打印描述性文字
char data_print[4096] = {0};
int cnt_p = 0;
int idx_p = 0;
for (cnt_p = 0; cnt_p < idx_d1; cnt_p++)
{
int len = strlen(data[cnt_p]);
memcpy(&data_print[idx_p], &data[cnt_p], len);
idx_p += len;
}
// 打印到控制台
printf("\033[31m[script error]\033[0m %s\n", data_print);
// 打印到文件中
// dzlog_error("\033[31m[script error]\033[0m %s\n", data_print);
}
int main()
{
// 打印数据
script_dzlog_error("type is_0:%s,%s %s string", "sssssss5", "ttttttttt6", "rrrrrr7");
script_dzlog_error("type is_5: no data");
}
三、可变参数函数的封装
实现目的:
1、理论上支持N种打印模式(目前已实现两种,可自行添加)
2、支持三种打印类型。分别是error、warning、debug类型
3、通过应用层代码控制打印位置。如打印到控制台或者文件中。
实现思路:
1、底层封装成库。剥离实现方式与实现逻辑。
2、应用层书写逻辑代码,用于指定打印实现与打印位置。
3、提供注册函数,用于把应用层逻辑传达给底层。
实现代码(忽略文件命名方式):
1、底层代码
script_debug.h文件
#ifndef __SCRIPT_DEBUG_H__
#define __SCRIPT_DEBUG_H__
#include
// 可变参数头文件
#include
#define SCRIPT_DEBUG 1
#ifdef SCRIPT_DEBUG
#define SCRIPT_DEBUG_ERR(fmt, args...) g_script_log.error(fmt, ##args)
#if (SCRIPT_DEBUG >= 1)
#define SCRIPT_DEBUG_WARN(fmt, args...) g_script_log.warni(fmt, ##args)
#else
#define SCRIPT_DEBUG_WARN(fmt,args...)
#endif
#if (SCRIPT_DEBUG >= 2)
#define SCRIPT_DEBUG_DEBUG(fmt, args...) g_script_log.debug(fmt, ##args)
#else
#define SCRIPT_DEBUG_DEBUG(fmt,args...)
#endif // #if (SCRIPT_DEBUG >= 2)
#endif // #ifdef SCRIPT_DEBUG
typedef void (*script_log_print)(char *fmt, ...);
// 支持打印类型
typedef struct script_log{
script_log_print error;
script_log_print warni;
script_log_print debug;
} script_log_t;
extern script_log_t g_script_log;
// 注册函数
int script_log_interface_register(script_log_print error, script_log_print warni, script_log_print debug);
#endif
script_debug.c文件
#include "script_debug.h"
#include
// 默认为打印到控制台
void script_log_error(char *fmt, ...)
{
// 定义可变参数列表
va_list ap;
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
// 打印可变参数
printf("\033[31m[script error]\033[0m ");
vprintf(fmt, ap);
// 结束
va_end(ap);
printf("\n");
}
void script_log_warni(char *fmt, ...)
{
// 定义可变参数列表
va_list ap;
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
// 打印可变参数
printf("\033[33m[script warni]\033[0m ");
vprintf(fmt, ap);
// 结束
va_end(ap);
printf("\n");
}
void script_log_debug(char *fmt, ...)
{
// 定义可变参数列表
va_list ap;
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
// 打印可变参数
printf("\033[34m[script debug]\033[0m ");
vprintf(fmt, ap);
// 结束
va_end(ap);
printf("\n");
}
// 初始化打印的位置为控制台
script_log_t g_script_log = {
.error = script_log_error,
.warni = script_log_warni,
.debug = script_log_debug,
};
// 实现功能:提供给vicenter注册
int script_log_interface_register(script_log_print error, script_log_print warni, script_log_print debug)
{
g_script_log.error = error;
g_script_log.warni = warni;
g_script_log.debug = debug;
}
2、应用层代码
script_print.h
#include "stdio.h"
#include
#include "script_debug.h"
#include "zlog.h"
char *my_itoa(int num, char *str);
char *my_ftoa(float num, char *str);
script_log_print script_dzlog_error(char *fmt, ...);
script_log_print script_dzlog_warni(char *fmt, ...);
script_log_print script_dzlog_debug(char *fmt, ...);
void parse_para(va_list ap, char *fmt, char *out_data);
script_print.c
#include "script_print.h"
// 打印位置:日志文件中
// 打印类型:error类型
script_log_print script_dzlog_error(char *fmt, ...)
{
va_list ap;
char out_data[4096] = {0};
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
parse_para(ap, fmt, out_data);
va_end(ap);
// 打印到控制台
printf("\033[31m[script error]\033[0m %s\n", out_data);
// 打印到文件中
dzlog_error("\033[31m[script error]\033[0m %s\n", out_data);
}
// 打印位置:日志文件中
// 打印类型:warni类型
script_log_print script_dzlog_warni(char *fmt, ...)
{
va_list ap;
char out_data[4096] = {0};
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
parse_para(ap, fmt, out_data);
va_end(ap);
printf("\033[33m[script warni]\033[0m %s\n", out_data);
dzlog_warn("\033[33m[script warni]\033[0m %s\n", out_data);
}
// 打印位置:日志文件中
// 打印类型:debug类型
script_log_print script_dzlog_debug(char *fmt, ...)
{
va_list ap;
char out_data[4096] = {0};
// 获取不定参数的首地址,首地址是从fmt开始
va_start(ap, fmt);
parse_para(ap, fmt, out_data);
va_end(ap);
printf("\033[34m[script debug]\033[0m %s\n", out_data);
dzlog_debug("\033[34m[script debug]\033[0m %s\n", out_data);
}
// 解析可变参数的数据,并把数据转换成字符串存储在out_data中
void parse_para(va_list ap, char *fmt, char *out_data)
{
char *p, *sval;
int ival;
double dval;
int idx_d1 = 0;
int cnt_v = 0;
// 存储每一段数据
char data[10][256] = {0};
// 存储修饰性字符
char data_val[256] = {0};
// 循环遍历每个字符
for (p = fmt; *p; p++)
{
if(*p != '%') {
// 获取修饰性字符
data_val[cnt_v++] = *p;
continue;
}
switch(*++p) {
case 'd':
{
ival = va_arg(ap, int);
char tmp[128] = {0};
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
my_itoa(ival, tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
case 'f':
{
dval = va_arg(ap, double);
char tmp[128] = {0};
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
my_ftoa(dval, tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
case 's':
{
char tmp[128] = {0};
char tmp_cnt = 0;
for (sval = va_arg(ap, char *); *sval; sval++)
{
// printf("%c", *sval);
tmp[tmp_cnt++] = *sval;
}
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
cnt_v = 0;
// 把数据型字符复制到data中
// printf("tmp:%s\n", tmp);
memcpy(&data[idx_d1++][0], tmp, sizeof(tmp));
break;
}
default:
data_val[cnt_v] = *p;
// putchar(*p);
break;
}
}
// 把修饰性字符复制到data中
memcpy(&data[idx_d1++][0], data_val, sizeof(data_val));
// printf("data_tail:%s\n", data_val);
// 清空data_val
memset(data_val, 0x00, sizeof(data_val));
// 打印描述性文字
// char data_print[4096] = {0};
int cnt_p = 0;
int idx_p = 0;
for (cnt_p = 0; cnt_p < idx_d1; cnt_p++)
{
int len = strlen(data[cnt_p]);
memcpy(&out_data[idx_p], &data[cnt_p], len);
idx_p += len;
}
}
char *my_itoa(int num, char *str)
{
if(str == NULL)
{
return NULL;
}
sprintf(str, "%d", num);
return str;
}
char *my_ftoa(float num, char *str)
{
if(str == NULL)
{
return NULL;
}
sprintf(str, "%.2lf", num);//将num 保留2位小数赋值给str
return str;
}
3、main.c文件
#include
#include "script_print.h"
extern int vicenter_cli_init(void);
int main()
{
// 注册打印方式-假如没有注册,则采用默认打印,底层已经实现。
script_log_interface_register(script_dzlog_error, script_dzlog_warni, script_dzlog_debug);
// 测试代码
SCRIPT_DEBUG_ERR("type is_0:%s,%s %s string11", "sssssss5", "ttttttttt6", "rrrrrr7");
SCRIPT_DEBUG_WARN("type is_1:%d %d,%d %d ddd3", 360, 508, 1000000, 1);
SCRIPT_DEBUG_DEBUG("type is_2:%f %f %f,ffff3", 362.01, 108.024, 50);
}
大功告成,奖励自己一下