C++ va_arg实现可变参数求最小值触发未处理读访问异常
问题现象
基于可变参数实现的多值求最小值C++程序运行时触发未捕获读访问冲突异常,崩溃点位于minim函数内取可变参数的element = va_arg(args, float);行,调试可见args指向非法地址0x600004。
原始问题代码:
#include<iostream> #include <stdarg.h> #include <math.h> using namespace std; float minim(int n,...); int main(void) { float v[10]; int n; cout << "Introduceti numerele reale: " << endl; for (int i = 0; i < 10; i++) { cout << "v[" << i + 1 << "]= "; cin >> v[i]; } cout << "\n"; cout << "Minimul dintre primele 7 numere introduse este: " << minim(7, v[0], v[1], v[2], v[3], v[4], v[5], v[6]) << endl; cout << "Minimul dintre ultimele 3 numere introduse este: " << minim(3, v[7], v[8], v[9]) << endl; cout << "Minimul dintre cele 10 numere este: " << minim(2, minim(6, v[0], v[1], v[2], v[3], v[4], v[5], v[6]), minim(3, v[7], v[8], v[9])) << endl; } float minim(int n, ...) { float element, minim=0; va_list args; va_start(args, n); for (int i = 0; i < n; i++) { element = va_arg(args, float); if (i = 0) minim = element; else if (element < minim) minim = element; } va_end(args); return minim; }
异常根因
崩溃由3处代码错误共同导致,前两处为致命错误:
- 可变参数取参类型违反默认参数提升规则:C/C++可变参数列表存在默认参数提升机制:传入的
float类型会被自动转换为double入栈,char/short会被自动转换为int入栈。代码中取参时指定类型为float,会按照4字节(float长度)偏移参数指针、按照float内存布局解析8字节的double数据,直接导致指针偏移错误、内存解析错位,读几个参数后指针就会指向非法内存区域。 - 相等判断笔误导致死循环越界读:初始化最小值的判断语句写为
if (i = 0),这是赋值操作而非相等比较,每次循环进入判断分支都会把循环变量i重新赋值为0,for循环的自增操作永远无法让i累加到n,循环会无限执行,持续越过参数列表的栈边界读取内存,直到撞到不可读的内存页触发访问冲突。 - 参数计数不匹配:计算10个数总最小值时,内层第一个
minim调用传入了7个浮点数,但第一个计数参数写为6,参数总数和计数不符,会进一步加剧越界读问题。
修复方案
对应3处错误逐一修正即可:
- 把可变参数取参的类型从
float改为double,匹配默认参数提升后的实际入栈类型,取到double值后隐式转换为float赋值即可; - 把
if (i = 0)修改为if (i == 0),修正相等判断笔误,消除死循环; - 修正总最小值计算时内层
minim调用的计数参数,把6改为7,和传入的7个数值参数匹配; - 清理冗余代码:删除main函数中未使用的
int n变量、未被调用的<math.h>头文件。
修复后的完整可运行代码:
#include<iostream> #include <stdarg.h> using namespace std; float minim(int n,...); int main(void) { float v[10]; cout << "Introduceti numerele reale: " << endl; for (int i = 0; i < 10; i++) { cout << "v[" << i + 1 << "]= "; cin >> v[i]; } cout << "\n"; cout << "Minimul dintre primele 7 numere introduse este: " << minim(7, v[0], v[1], v[2], v[3], v[4], v[5], v[6]) << endl; cout << "Minimul dintre ultimele 3 numere introduse este: " << minim(3, v[7], v[8], v[9]) << endl; cout << "Minimul dintre cele 10 numere este: " << minim(2, minim(7, v[0], v[1], v[2], v[3], v[4], v[5], v[6]), minim(3, v[7], v[8], v[9])) << endl; return 0; } float minim(int n, ...) { float element, minim_val = 0; va_list args; va_start(args, n); for (int i = 0; i < n; i++) { // 可变参数float默认提升为double,必须用double取参 element = (float)va_arg(args, double); if (i == 0) minim_val = element; else if (element < minim_val) minim_val = element; } va_end(args); return minim_val; }
注:C++更推荐使用可变参数模板、初始化列表等类型安全的方式实现变长参数求最小值,避免C风格可变参数的类型安全问题。
内容的提问来源于stack exchange,提问作者Fdsfds Fdsfds
相关产品推荐
相关产品推荐

