You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言可变参数函数指针实现:封装调用函数并传递参数

解决方案

一、实现接收函数与参数并调用的功能

你的代码无法运行的核心原因是:普通函数(如bar)不能直接接收va_list作为参数,va_list是为可变参数函数(带...的函数)设计的,无法直接展开为普通函数的参数列表。下面提供三种不同场景的解决方案:

1. 最简单的宏实现(推荐,无编译器依赖)

如果不需要用函数实现,宏可以直接展开参数列表,完全符合你的需求:

#include <stdio.h>

#define foo(func, ...) func(__VA_ARGS__)

void bar(int a, char *b) {
    printf("a = %d, b = %s\n", a, b);
}

int main() {
    foo(bar, 2, "hello"); // 等价于直接调用 bar(2, "hello")
    return 0;
}

2. 标准C函数实现(需为每个目标函数写包装)

如果必须用函数实现,标准C中只能通过手动解析va_list,为每个目标函数编写对应的包装函数:

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

// 定义通用的包装函数指针类型
typedef void (*WrappedFunc)(va_list);

// 为bar编写的包装函数:解析va_list并调用原函数
void bar_wrapper(va_list args) {
    int a = va_arg(args, int);
    char *b = va_arg(args, char*);
    bar(a, b);
}

// 通用调用函数
void foo(WrappedFunc func, ...) {
    va_list args;
    va_start(args, func);
    func(args); // 调用包装函数,由它解析参数并调用目标函数
    va_end(args);
}

void bar(int a, char *b) {
    printf("a = %d, b = %s\n", a, b);
}

int main() {
    foo(bar_wrapper, 2, "hello");
    return 0;
}

3. GCC编译器扩展实现(无需包装函数,但非标准)

如果你使用GCC或Clang,可以利用编译器内置函数直接传递可变参数,无需手动解析:

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

void foo(void (*function)(...), ...) {
    va_list args;
    va_start(args, function);
    // 使用GCC扩展传递参数
    __builtin_apply((void(*)())function, args, __builtin_saveregs());
    va_end(args);
}

void bar(int a, char *b) {
    printf("a = %d, b = %s\n", a, b);
}

int main() {
    foo(bar, 2, "hello");
    return 0;
}

二、记录标准输入输出用于单元测试

可以通过将stdin/stdout重定向到内存缓冲区来捕获输出,下面是针对stdout的简洁实现(POSIX兼容,Linux/macOS可用):

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

// 捕获stdout输出的函数
char* capture_stdout(void (*func)(void), size_t *output_len) {
    char *buffer = NULL;
    size_t buf_size = 0;
    // 创建内存流,写入的数据会自动分配到buffer中
    FILE *mem_stream = open_memstream(&buffer, &buf_size);
    if (!mem_stream) return NULL;

    // 保存原stdout,替换为内存流
    FILE *old_stdout = stdout;
    stdout = mem_stream;

    // 调用目标函数
    func();

    // 恢复原stdout并清理
    fflush(stdout);
    stdout = old_stdout;
    fclose(mem_stream);

    *output_len = buf_size;
    return buffer; // 调用者需手动free该内存
}

// 测试用函数
void test_bar_call() {
    bar(2, "hello");
}

int main() {
    size_t len;
    char *captured = capture_stdout(test_bar_call, &len);
    if (captured) {
        printf("捕获的输出内容:\n%s", captured);
        free(captured);
    }
    return 0;
}

如果需要结合之前的foo函数,可以修改capture_stdout的参数,或者用宏包装调用逻辑。


内容的提问来源于stack exchange,提问作者Saeedou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 04:53:12