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
相关产品推荐
相关产品推荐

