Skip to content

C 标准库 - <stdarg.h>

概述

<stdarg.h> 头文件提供了处理可变参数函数的机制,允许函数接受可变数量的参数。这是实现类似 printf() 这样的函数的基础。

可变参数类型

va_list

用于保存可变参数信息的类型。

c
typedef struct {
} va_list;

可变参数宏

va_start()

c
void va_start(va_list ap, last_arg);

初始化可变参数列表。

c
#include <stdio.h>
#include <stdarg.h>

void print_numbers(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        int num = va_arg(args, int);
        printf("%d ", num);
    }
    
    va_end(args);
    printf("\n");
}

int main() {
    print_numbers(3, 10, 20, 30);
    return 0;
}

va_arg()

c
type va_arg(va_list ap, type);

获取下一个可变参数。

c
#include <stdio.h>
#include <stdarg.h>

double average(int count, ...) {
    va_list args;
    va_start(args, count);
    
    double sum = 0.0;
    for (int i = 0; i < count; i++) {
        sum += va_arg(args, double);
    }
    
    va_end(args);
    return sum / count;
}

int main() {
    double avg = average(4, 10.5, 20.5, 30.5, 40.5);
    printf("平均值: %.2f\n", avg);
    return 0;
}

va_end()

c
void va_end(va_list ap);

清理可变参数列表。

c
#include <stdio.h>
#include <stdarg.h>

void print_strings(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        char* str = va_arg(args, char*);
        printf("%s\n", str);
    }
    
    va_end(args);
}

int main() {
    print_strings(3, "Hello", "World", "C");
    return 0;
}

va_copy()

c
void va_copy(va_list dest, va_list src);

复制可变参数列表(C99)。

c
#include <stdio.h>
#include <stdarg.h>

void process_args(int count, ...) {
    va_list args1, args2;
    
    va_start(args1, count);
    va_copy(args2, args1);
    
    printf("第一次遍历:\n");
    for (int i = 0; i < count; i++) {
        int num = va_arg(args1, int);
        printf("  %d\n", num);
    }
    
    printf("第二次遍历:\n");
    for (int i = 0; i < count; i++) {
        int num = va_arg(args2, int);
        printf("  %d\n", num);
    }
    
    va_end(args1);
    va_end(args2);
}

int main() {
    process_args(3, 10, 20, 30);
    return 0;
}

实际应用示例

1. 自定义 printf

c
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

void my_printf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    
    while (*format != '\0') {
        if (*format == '%') {
            format++;
            
            switch (*format) {
                case 'd':
                    printf("%d", va_arg(args, int));
                    break;
                case 'f':
                    printf("%f", va_arg(args, double));
                    break;
                case 's':
                    printf("%s", va_arg(args, char*));
                    break;
                case 'c':
                    printf("%c", va_arg(args, int));
                    break;
                case '%':
                    printf("%%");
                    break;
                default:
                    printf("未知格式: %c", *format);
            }
        } else {
            putchar(*format);
        }
        
        format++;
    }
    
    va_end(args);
}

int main() {
    my_printf("整数: %d\n", 42);
    my_printf("浮点数: %f\n", 3.14);
    my_printf("字符串: %s\n", "Hello");
    my_printf("字符: %c\n", 'A');
    my_printf("混合: %d %f %s %c\n", 10, 2.5, "World", 'B');
    
    return 0;
}

2. 求和函数

c
#include <stdio.h>
#include <stdarg.h>

int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }
    
    va_end(args);
    return total;
}

int main() {
    int result1 = sum(3, 10, 20, 30);
    printf("sum(10, 20, 30) = %d\n", result1);
    
    int result2 = sum(5, 1, 2, 3, 4, 5);
    printf("sum(1, 2, 3, 4, 5) = %d\n", result2);
    
    return 0;
}

3. 查找最大值

c
#include <stdio.h>
#include <stdarg.h>

int max(int count, ...) {
    va_list args;
    va_start(args, count);
    
    int maximum = va_arg(args, int);
    for (int i = 1; i < count; i++) {
        int value = va_arg(args, int);
        if (value > maximum) {
            maximum = value;
        }
    }
    
    va_end(args);
    return maximum;
}

int main() {
    int result1 = max(4, 10, 20, 30, 15);
    printf("max(10, 20, 30, 15) = %d\n", result1);
    
    int result2 = max(5, 5, 2, 8, 1, 3);
    printf("max(5, 2, 8, 1, 3) = %d\n", result2);
    
    return 0;
}

4. 字符串连接

c
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

char* concat(int count, ...) {
    va_list args;
    va_start(args, count);
    
    int total_length = 0;
    for (int i = 0; i < count; i++) {
        char* str = va_arg(args, char*);
        total_length += strlen(str);
    }
    
    char* result = malloc(total_length + 1);
    if (result == NULL) {
        va_end(args);
        return NULL;
    }
    
    result[0] = '\0';
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        char* str = va_arg(args, char*);
        strcat(result, str);
    }
    
    va_end(args);
    return result;
}

int main() {
    char* result = concat(3, "Hello", " ", "World");
    printf("连接结果: %s\n", result);
    free(result);
    
    return 0;
}

5. 错误日志

c
#include <stdio.h>
#include <stdarg.h>
#include <time.h>

void log_error(const char* format, ...) {
    time_t now = time(NULL);
    struct tm* tm = localtime(&now);
    
    printf("[%04d-%02d-%02d %02d:%02d:%02d] ERROR: ",
           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
           tm->tm_hour, tm->tm_min, tm->tm_sec);
    
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    
    printf("\n");
}

int main() {
    log_error("文件未找到: %s", "config.txt");
    log_error("内存分配失败,需要: %d 字节", 1024);
    log_error("连接超时,服务器: %s:%d", "example.com", 8080);
    
    return 0;
}

6. 数组创建

c
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

int* create_array(int count, ...) {
    int* array = malloc(count * sizeof(int));
    if (array == NULL) {
        return NULL;
    }
    
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        array[i] = va_arg(args, int);
    }
    
    va_end(args);
    return array;
}

void print_array(int* array, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

int main() {
    int* arr = create_array(5, 10, 20, 30, 40, 50);
    if (arr != NULL) {
        print_array(arr, 5);
        free(arr);
    }
    
    return 0;
}

7. 多类型处理

c
#include <stdio.h>
#include <stdarg.h>

typedef enum {
    TYPE_INT,
    TYPE_DOUBLE,
    TYPE_STRING,
    TYPE_CHAR
} Type;

void print_values(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        Type type = va_arg(args, Type);
        
        switch (type) {
            case TYPE_INT:
                printf("int: %d\n", va_arg(args, int));
                break;
            case TYPE_DOUBLE:
                printf("double: %f\n", va_arg(args, double));
                break;
            case TYPE_STRING:
                printf("string: %s\n", va_arg(args, char*));
                break;
            case TYPE_CHAR:
                printf("char: %c\n", va_arg(args, int));
                break;
        }
    }
    
    va_end(args);
}

int main() {
    print_values(6,
               TYPE_INT, 42,
               TYPE_DOUBLE, 3.14,
               TYPE_STRING, "Hello",
               TYPE_CHAR, 'A',
               TYPE_INT, 100,
               TYPE_DOUBLE, 2.5);
    
    return 0;
}

8. 可变参数递归

c
#include <stdio.h>
#include <stdarg.h>

int recursive_sum(int count, ...) {
    va_list args;
    va_start(args, count);
    
    if (count == 0) {
        va_end(args);
        return 0;
    }
    
    int first = va_arg(args, int);
    
    va_list args_copy;
    va_copy(args_copy, args);
    
    int rest = recursive_sum(count - 1);
    
    va_end(args_copy);
    va_end(args);
    
    return first + rest;
}

int main() {
    int result = recursive_sum(5, 1, 2, 3, 4, 5);
    printf("递归求和: %d\n", result);
    
    return 0;
}

注意事项

1. 必须有固定参数

可变参数函数必须至少有一个固定参数。

c
#include <stdio.h>
#include <stdarg.h>

void valid_function(int count, ...) {
    va_list args;
    va_start(args, count);
    va_end(args);
}

void invalid_function(...) {
    va_list args;
    va_start(args, ???);
    va_end(args);
}

int main() {
    valid_function(3, 1, 2, 3);
    return 0;
}

2. 参数类型匹配

确保使用正确的类型获取参数。

c
#include <stdio.h>
#include <stdarg.h>

void print_values(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        int value = va_arg(args, int);
        printf("%d ", value);
    }
    
    va_end(args);
    printf("\n");
}

int main() {
    print_values(3, 10, 20, 30);
    print_values(3, 10.5, 20.5, 30.5);
    
    return 0;
}

3. 必须调用 va_end()

每次使用 va_start 后必须调用 va_end。

c
#include <stdio.h>
#include <stdarg.h>

void good_function(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        int value = va_arg(args, int);
        printf("%d ", value);
    }
    
    va_end(args);
    printf("\n");
}

void bad_function(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        int value = va_arg(args, int);
        printf("%d ", value);
    }
    
    printf("\n");
}

int main() {
    good_function(3, 10, 20, 30);
    bad_function(3, 10, 20, 30);
    
    return 0;
}

4. 参数数量限制

需要一种方法确定参数的数量。

c
#include <stdio.h>
#include <stdarg.h>

void print_with_count(int count, ...) {
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        int value = va_arg(args, int);
        printf("%d ", value);
    }
    
    va_end(args);
    printf("\n");
}

void print_with_sentinel(...) {
    va_list args;
    va_start(args, 0);
    
    int value;
    while ((value = va_arg(args, int)) != -1) {
        printf("%d ", value);
    }
    
    va_end(args);
    printf("\n");
}

int main() {
    print_with_count(3, 10, 20, 30);
    print_with_sentinel(10, 20, 30, -1);
    
    return 0;
}

总结

<stdarg.h> 提供的可变参数机制是 C 语言中实现灵活函数的重要工具:

  1. 灵活性高 - 函数可以接受不同数量的参数
  2. 功能强大 - 可以实现类似 printf 的函数
  3. 应用广泛 - 用于日志、格式化输出、数据处理等

记住:

  • 必须至少有一个固定参数
  • 确保使用正确的类型获取参数
  • 每次使用 va_start 后必须调用 va_end
  • 需要一种方法确定参数的数量