实现返回传入参数个数的可变参数函数失败,求原因分析
为什么你的可变参数函数返回了错误的参数个数?
你的代码里有几个关键问题导致了这个离谱的结果,咱们一步步拆解清楚:
1. 错误的终止逻辑与参数读取方式
你试图用nullptr作为可变参数的结束标记,但这里有两个致命问题:
- 调用时根本没传终止标记:比如你传入两个参数
SIZE(1, 2),栈里并没有结尾的nullptr,函数会一直从栈里读取后续的垃圾数据,直到碰巧遇到一个值为0的内存地址(也就是nullptr)——这就是为什么返回了12,它读了12个栈里的无效数据才碰到这个“假的”终止信号。 va_arg的类型完全不匹配:可变参数的读取必须严格对应实际参数的类型。如果你传入的是int类型,却用void*去读取,会直接破坏栈指针的偏移逻辑。比如在64位系统中,int是4字节,void*是8字节,每次调用va_arg都会多跳4字节,后续读取的全是错位的垃圾值,完全偏离了真实参数的位置。
2. 浪费了第一个参数n的作用
你的函数第一个参数是int n,但代码里完全没用到它。通常可变参数函数如果不用终止标记,就会用第一个参数来明确指定参数的个数(比如很多底层函数的设计思路),这才是可靠的做法,完全不需要依赖栈里的随机值。
正确的实现方式
如果你的需求是返回传入的参数个数,有两种靠谱的写法:
方式一:用第一个参数指定个数(最推荐)
#include <stdarg.h> int SIZE(int n, ...) { va_list args; va_start(args, n); // 已知个数是n,直接遍历跳过所有参数即可 for (int i = 0; i < n; i++) { va_arg(args, int); // 这里要和实际传入的参数类型匹配,混合类型需要额外处理 } va_end(args); return n; }
调用时要传对个数:SIZE(2, 10, 20),会准确返回2。
方式二:用终止标记(仅限特定场景)
如果你坚持用终止标记,必须确保最后一个参数是nullptr,且所有参数能安全转为void*(比如指针类型,32位系统中int和指针大小相同也可以,但64位不推荐):
#include <stdarg.h> int SIZE(void* first_arg, ...) { if (first_arg == nullptr) return 0; int length = 1; va_list args; va_start(args, first_arg); void* current_arg; while ((current_arg = va_arg(args, void*)) != nullptr) { length++; } va_end(args); return length; }
调用时必须加终止标记:SIZE((void*)10, (void*)20, nullptr),返回2。但这种方式局限性很大,不适合非指针类型的参数。
总结
你的核心问题是没有遵守可变参数的读取规则,也没设置有效的终止条件,导致函数越界读取栈内存,返回了随机的垃圾值。用第一个参数指定个数是最稳妥的方案,终止标记只适合特定的指针场景。
内容的提问来源于stack exchange,提问作者Gabel Luc
相关产品推荐
相关产品推荐

