C++打印数组输出不存在的异常值 排序后异常消失问题咨询
实现程序逻辑:读取两个数组,第一个数组长度为C,第二个数组长度为N,读取完成后打印两个数组内容。实际运行时打印出的数组包含从未写入的异常数值;如果在打印前先对数组执行排序操作,这些异常数值就不会出现。
复现环境:使用Clang、GNU编译器编译运行均出现同类异常,当C=10、N=10,两个数组输入值均为{1,2,3,4,5,6,7,8,9,10}时,程序实际输出如下:
第一数组: 1 2 3 4 5 6 7 8 9 10 4199003 0
排序后第一数组: 1 2 3 4 5 6 7 8 9 10
第二数组: 1 2 3 4 5 6 7 8 9 10 -1159496120 32765 1 2 3 4 5 6 7 8 9 10
排序后第二数组: 1 2 3 4 5 6 7 8 9 10
异常复现代码:
#include <iostream> #include <algorithm> using namespace std; void println(int arr[]) { for (int i = 0; i < (&arr)[1] - arr; ++i) { cout << arr[i] << " "; } cout << std::endl; } int main () { int c, n; cin >> c >> n; int t[c], a[n]; for (int i = 0; i < c; ++i) { cin >> t[i]; } for (int i = 0; i < n; ++i) { cin >> a[i]; } cout << "First array: "; println(t); sort(t, t+c); cout << "First array sorted: "; println(t); cout << "Second array: "; println(a); cout << "Second array sorted: "; sort(a, a+n); println(a); }
异常出现的核心是两处代码错误共同导致的未定义行为:
1. println函数的数组长度计算逻辑完全失效
C++中数组作为函数形参传递时,会自动退化为对应元素类型的指针,你写的void println(int arr[])本质等价于void println(int* arr)——形参arr只是一个存储数组首地址的指针变量,本身不携带任何数组长度信息。
你使用的(&arr)[1] - arr仅在arr是当前作用域内定义的栈数组时,才可能(依赖编译器实现)算出正确长度,在数组退化为指针的场景下完全不成立:
&arr取到的是println函数内这个指针形参自身的栈地址,和main函数中定义的原数组没有关联(&arr)[1]是该指针地址往后偏移一个指针长度的内存位置,这个位置存储的是栈上的随机值,根本不是原数组的结束边界- 用这个随机值减去数组首地址算出的循环边界远大于数组实际长度,循环会越界访问数组后方的栈内存,你看到的奇怪数值就是这部分未被主动写入、存储了其他栈数据/未初始化垃圾值的内存内容。
2. 使用了非标准的变长数组语法
代码中int t[c], a[n]属于变长数组(VLA),这不是C++标准规定的语法,只是GCC、Clang提供的编译器扩展,其栈内存布局完全依赖编译器实现,没有统一标准约束,进一步放大了越界访问结果的不确定性。
这个现象完全是未定义行为下的巧合,不代表问题被修复:
你调用sort时传入了正确的数组边界t, t+c和a, a+n,排序本身只会操作数组合法长度内的元素,不会主动修改越界内存。但sort函数执行过程中会产生栈临时变量、修改寄存器状态,刚好覆盖了栈上之前被越界读取的内存位置,让你那个错误的长度计算式刚好算出和数组真实长度一致的循环上限,因此没有打印出垃圾值。只要编译器版本、编译选项、输入值稍有变化,这个看似正常的现象就会消失,越界访问的问题始终存在。
- 彻底放弃在函数内靠指针计算数组长度的错误写法,处理数组时必须显式传入数组长度作为参数。C++中数组退化为指针后,没有任何通用方法可以从指针本身反推原数组长度。
- 弃用非标准的变长数组,改用C++标准提供的
std::vector存储动态长度的序列,从根源避免VLA带来的未定义行为。
修复后的参考代码:
#include <iostream> #include <algorithm> #include <vector> using namespace std; void println(const int arr[], int len) { for (int i = 0; i < len; ++i) { cout << arr[i] << " "; } cout << endl; } int main () { int c, n; cin >> c >> n; vector<int> t(c), a(n); for (int i = 0; i < c; ++i) { cin >> t[i]; } for (int i = 0; i < n; ++i) { cin >> a[i]; } cout << "First array: "; println(t.data(), c); sort(t.begin(), t.end()); cout << "First array sorted: "; println(t.data(), c); cout << "Second array: "; println(a.data(), n); sort(a.begin(), a.end()); cout << "Second array sorted: "; println(a.data(), n); return 0; }
内容的提问来源于stack exchange,提问作者zxcvbnm

