C/C++中,数组越界访问是指尝试访问数组范围之外的元素。数组越界访问 是 C/C++ 程序中非常常见且危险的一种错误。当程序试图访问数组索引超出数组合法范围的元素时,就会发生数组越界。这种行为会导致不可预期的后果,甚至导致程序崩溃。

1、数组越界” (ABOB)

“数组越界”指的是当访问数组时,索引超出了数组的有效范围。通常发生在以下情况,索引小于 0。索引大于或等于数组的大小。如有一个大小为10的数组,有效的索引是从09。如果你访问了数组的索引为10或更高,或者是负数,这就是越界访问。

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    // 正确的访问方式
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    // 错误的访问方式(越界访问)
    // printf("%d\n", arr[5]);  // 这是越界的,因为有效的索引是 0 到 4
    
    return 0;
}

2、 访问越界的内存

C和C++中,访问越界的数组会导致未定义行为。也就是可能会:崩溃(例如,发生段错误),返回一个意外的值(垃圾数据或错误数据),看起来正常工作,但导致难以察觉的bug,损坏内存,导致程序后续发生其他错误。访问数组越界后的行为是没有保障的,程序的表现可能会因编译器、系统架构或错误发生的上下文而有所不同。

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    // 访问越界的索引
    printf("%d\n", arr[100]);  // 未定义行为:访问越界的索引

    return 0;
}

3、数组越界是未定义的原因

C和C++中,数组没有自动进行边界检查。也就意味着,语言本身或编译器不会强制检查数组越界。虽然可能希望在访问越界索引时程序抛出错误,但编译器通常不会检查数组边界,以避免性能开销。语言标准明确将越界访问定义为未定义行为。这是为了优化性能和提供更大的灵活性。

#include <iostream>
using namespace std;

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    // 正常访问数组元素
    // 输出 arr[2],即 3
    cout << "arr[2]: " << arr[2] << endl; 

    // 越界访问
    // 未定义行为,访问数组边界外的内存
    cout << "arr[10]: " << arr[10] << endl;  

    return 0;
}

4、常见的数组越界错误

访问数组越界后,可能会出现垃圾数据,返回的值可能是随机或垃圾数据。段错误(崩溃),如果访问的地址完全无效(例如,超出了已分配的内存空间),程序可能崩溃。内存损坏,如果越界访问修改了其他变量或结构体使用的内存,可能会导致数据损坏,从而在程序后续发生错误。

#include <stdio.h>

int main() {
    // 定义一个长度为3的整数数组
    int arr[3] = {1, 2, 3};

    // 越界访问,尝试访问 arr[10]
    arr[10] = 100;  // 这将写入 arr[10],但 arr 只有 3 个元素,越界写入会导致未定义行为,可能损坏内存

    // 打印 arr[0],其值可能由于上面的内存损坏而被更改
    printf("arr[0] = %d\n", arr[0]);

    return 0;
}

5、防止越界访问

尽管C和C++默认不会自动检查数组边界,但可以采取一些措施来防止越界错误。

1)手动检查数组边界

在访问数组之前,确保索引在有效范围内。

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);

    int index = 2;  // 假设用户输入的索引
    if (index >= 0 && index < size) {
        printf("数组元素: %d\n", arr[index]);
    } else {
        printf("索引越界!\n");
    }

    // 测试索引越界的情况
    index = 5;  // 索引超出数组范围
    if (index >= 0 && index < size) {
        printf("数组元素: %d\n", arr[index]);
    } else {
        printf("索引越界!\n");
    }

    return 0;
}

2)使用C++中的std::vector

与数组不同,C++中的std::vector使用at()方法进行边界检查(如果索引越界会抛出异常)。

#include <iostream>
#include <vector>
#include <stdexcept>  // for std::out_of_range
using namespace std;

int main() {
    // 创建一个整数类型的vector
    vector<int> vec = {1, 2, 3};
    
    try {
        // 尝试访问vec中不存在的元素(越界)
        // 会抛出std::out_of_range异常
        cout << vec.at(5) << endl; 
    } catch (const std::out_of_range& e) {
        // 捕获异常并打印错误信息
        cout << "越界错误: " << e.what() 
        << endl;
    }

    return 0;
}

推荐文档