C语言可变参数函数和可变参数宏
可变参数函数
在C语言中,可变参数函数(Variable Argument Function)是一种特殊的函数形式,它可以接受不定数量的参数。这类函数允许调用者在调用时传递任意数量的参数,这对于某些场景非常有用,例如日志记录、格式化输出等。
常见的可变参数函数
C语言标准库中有一些常用的可变参数函数,包括但不限于:
printf
:格式化输出函数,可以接受不定数量的参数。fprintf
:向指定流格式化输出,可以接受不定数量的参数。sprintf
:将格式化输出写入到字符串中,可以接受不定数量的参数。snprintf
:将格式化输出写入到字符串中,指定最大长度,可以接受不定数量的参数。vprintf
:内部使用的可变参数格式化输出函数,通常不直接调用。vfprintf
:内部使用的可变参数格式化输出函数,通常不直接调用。vsprintf
:内部使用的可变参数格式化输出函数,通常不直接调用。vsnprintf
:内部使用的可变参数格式化输出函数,通常不直接调用。scanf
:从标准输入读取格式化输入,可以接受不定数量的参数。fscanf
:从指定流读取格式化输入,可以接受不定数量的参数。sscanf
:从字符串读取格式化输入,可以接受不定数量的参数。
可变参数函数的原理
在C语言中,可变参数函数依赖于两个宏定义:va_start
、va_list
、va_arg
和 va_end
。这些宏定义定义在 <stdarg.h>
头文件中。它们的工作原理如下:
- va_list:定义一个类型为
va_list
的变量,用于保存可变参数列表的状态。 - va_start:宏
va_start
初始化va_list
变量,准备从可变参数列表的第一个参数开始处理。 - va_arg:宏
va_arg
获取下一个参数,并返回该参数的值。 - va_end:宏
va_end
清理va_list
变量。
可变参数函数的定义
可变参数函数的定义通常包含一个固定参数列表,后面跟着一个省略号 ...
。省略号表示可以有任意数量的附加参数。
#include <stdarg.h>
#include <stdio.h>
// 定义一个可变参数函数
void __attribute__((format(printf,1,2))) my_printf(char *fmt,...)
{
va_list args;
va_start(args,fmt);
vprintf(fmt,args);
va_end(args);
}
int main() {
my_printf("Hello, %s! Today is %s.\n", "world", "Monday");
return 0;
}
运行结果:
Hello, world! Today is Monday.
可变参数函数实现日志系统
实现日志分级
#include <stdio.h>
#include <stdarg.h>
// 定义日志级别
typedef enum {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
LOG_LEVEL_NONE
} LogLevel;
// 编译时指定的日志级别,可以通过宏定义在编译时设置
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_LEVEL_DEBUG
#endif
// 日志函数
void __attribute__((format(printf,2,3))) DEBUG_PRINT(LogLevel level, const char *format, ...) {
va_list args;
const char *level_strings[] = {"DEBUG", "INFO", "WARNING", "ERROR"};
if (level >= LOG_LEVEL) {
va_start(args, format);
printf("[%s] ", level_strings[level]);
vprintf(format, args);
printf("\n");
va_end(args);
}
}
// 示例使用
int main() {
DEBUG_PRINT(LOG_LEVEL_DEBUG, "This is a debug message.");
DEBUG_PRINT(LOG_LEVEL_INFO, "This is an info message.");
DEBUG_PRINT(LOG_LEVEL_WARNING, "This is a warning message. %d, %s, %.2f, %d", 2, __func__, 3.14, __LINE__);
DEBUG_PRINT(LOG_LEVEL_ERROR, "This is an error message.");
return 0;
}
编译:
gcc main.c -DLOG_LEVEL=LOG_LEVEL_INFO
运行结果:
[INFO] This is an info message.
[WARNING] This is a warning message. 2, main, 3.14, 36
[ERROR] This is an error message.
可变参数宏
__VA_ARGS__
#include <stdio.h>
#define PRINT(...) printf(__VA_ARGS__)
int main() {
int a = 10;
char *str = "Hello, World!";
PRINT("This is a normal message.\n");
PRINT("a = %d, str = %s\n", a, str);
return 0;
}
利用可变参数宏改造上面利用可变参数函数实现的日志系统
#include <stdio.h>
#include <stdarg.h>
// 定义日志级别
typedef enum {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
LOG_LEVEL_NONE
} LogLevel;
// 编译时指定的日志级别,可以通过宏定义在编译时设置
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_LEVEL_DEBUG
#endif
// 日志函数
void log_message(LogLevel level, const char *format, ...) {
va_list args;
const char *level_strings[] = {"DEBUG", "INFO", "WARNING", "ERROR"};
if (level >= LOG_LEVEL) {
va_start(args, format);
printf("[%s] ", level_strings[level]);
vprintf(format, args);
printf("\n");
va_end(args);
}
}
// 定义宏,用于不同的日志级别
#define LOG_DEBUG(format, ...) log_message(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) log_message(LOG_LEVEL_INFO, format, ##__VA_ARGS__)
#define LOG_WARNING(format, ...) log_message(LOG_LEVEL_WARNING, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) log_message(LOG_LEVEL_ERROR, format, ##__VA_ARGS__)
// 示例使用
int main() {
LOG_DEBUG("This is a debug message.");
LOG_INFO("This is an info message.");
LOG_WARNING("This is a warning message.");
LOG_ERROR("This is an error message.");
return 0;
}
编译:
gcc main.c -DLOG_LEVEL=LOG_LEVEL_INFO
运行结果:
[INFO] This is an info message.
[WARNING] This is a warning message.
[ERROR] This is an error message.