在c语言中,我们常用的printf()函数就使用到了可变参数。那他是什么呢?
#include <stdio.h>
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中。
-
- 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;
- }