在c语言中,我们常用的printf()函数就使用到了可变参数。那他是什么呢?
#include
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
可以看到printf函数家族的最后一个参数都是“...”。那这个“...”参数是什么意思呢?
看一下我们平时使用printf函数的格式
const char* str="hello world";
int a=1;
printf("%s %d\n", str,a);
const char *format参数就是前面的“%s %d\n”这样的字符串,...参数就是可变参数也就是说你可以指定任意参数任意类型。
这意味着printf函数与其他函数都有所不同,普通函数都指明了参数个数以及参数类型,但是printf函数不同,他会根据你输入的参数个数以及参数类型打印出结果。那么他是如何做到的呢?
1.printf函数会根据format中的%s或者%d,%x%p这样的字符来判断后面的参数是什么类型,从而准确打印。2. printf函数会使用 va_list p指明一个参数指针p,va_arg(p,int)根据类型提取参数,va_start(p,format)让p指向可变参数部分的起始地址,va_end(p)这样的函数来把参数指针置空,这样就保证了可变参数的完全打印,不会漏掉某个参数;
简单日志demo
主要是为了理解日志的思想,利用了可变参数,使得程序的测试结果标准化,可视化,简易化,减轻了程序员负担。
下面的日志格式: 日志等级 时间 进程pid 消息体。
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
// 日志是有日志等级的
const std::string filename = "log/tcpserver.log";
enum
{
Debug = 0,
Info,
Warning,
Error,
Fatal,
Uknown
};
static std::string toLevelString(int level)
{
switch (level)
{
case Debug:
return "Debug";
case Info:
return "Info";
case Warning:
return "Warning";
case Error:
return "Error";
case Fatal:
return "Fatal";
default:
return "Uknown";
}
}
static std::string getTime()
{
time_t curr = time(nullptr);
struct tm *tmp = localtime(&curr);
char buffer[128];
snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d", tmp->tm_year + 1900, tmp->tm_mon+1, tmp->tm_mday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
return buffer;
}
// 日志格式: 日志等级 时间 pid 消息体
// logMessage(DEBUG, "hello: %d, %s", 12, s.c_str()); // DEBUG hello:12, world
void logMessage(int level, const char *format, ...)
{
char logLeft[1024];
std::string level_string = toLevelString(level);
std::string curr_time = getTime();
snprintf(logLeft, sizeof(logLeft), "[%s] [%s] [%d] ", level_string.c_str(), curr_time.c_str(), getpid());
char logRight[1024];
va_list p;
va_start(p, format);
vsnprintf(logRight, sizeof(logRight), format, p);
va_end(p);
// 打印
// printf("%s%s\n", logLeft, logRight);
// 保存到文件中
FILE *fp = fopen(filename.c_str(), "a");
if(fp == nullptr)return;
fprintf(fp,"%s%s\n", logLeft, logRight);
fflush(fp); //可写也可以不写
fclose(fp);
// 预备
// va_list p; // char *
// int a = va_arg(p, int); // 根据类型提取参数
// va_start(p, format); //p指向可变参数部分的起始地址
// va_end(p); // p = NULL;
}