C++避坑-数组越界:看似安全其实危险

内容分享2周前发布
0 0 0

C++避坑-数组越界:看似安全实则危险

您在使用c++的过程中遇到过哪些”坑”呢?欢迎在留言区分享讨论,也欢迎关注收藏,多谢.

数组越界是另一个”编译通过,运行时爆炸”的经典陷阱。C++不会检查数组边界,它会让你访问数组外的内存,然后你就要面对未定义行为了。

最简单的越界:

int arr[5] = {1, 2, 3, 4, 5};
std::cout << arr[10] << std::endl;  // 越界!但编译器不说

程序可能正常运行(读到垃圾值),也可能崩溃,也可能破坏其他内存导致程序行为异常。这就是未定义行为的魅力。

最常见的错误是在循环里算错了边界:

int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; ++i) {  // 应该是 i < 5
    std::cout << arr[i] << std::endl;
}

i <= 5多了一次迭代,最后一次访问arr[5]越界了。

更隐蔽的情况:

void processArray(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        if (arr[i] > 0) {
            arr[i + 1] = arr[i] * 2;  // 如果i是最后一个元素,越界了!
        }
    }
}

C风格字符串有个额外的陷阱:

char buffer[10];
strcpy(buffer, "Hello, World!");  // 字符串长度超过buffer,越界了!

更安全的方式:

char buffer[10];
strncpy(buffer, "Hello, World!", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '';  // 确保null终止

但最好的方式是用std::string:

std::string buffer = "Hello, World!";  // 自动管理大小

std::vector至少提供了at()方法来检查边界:

#include <vector>
#include <stdexcept>

std::vector<int> vec = {1, 2, 3, 4, 5};

try {
    int value = vec.at(10);  // 抛出std::out_of_range异常
} catch (const std::out_of_range& e) {
    std::cout << "Index out of range!
";
}

// 但[]操作符还是不检查
int value = vec[10];  // 依旧是未定义行为

at()有性能开销,所以提供了不检查的[]。但至少给你一个选择。

C++11的范围for循环帮你避免越界:

std::vector<int> vec = {1, 2, 3, 4, 5};

for (auto& value : vec) {
    std::cout << value << std::endl;  // 不可能越界
}

但要注意,在循环里修改容器可能导致迭代器失效:

for (auto& value : vec) {
    if (value > 3) {
        vec.push_back(value * 2);  // 危险!可能导致重新分配
    }
}

多维数组的索引更容易出错:

int matrix[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

// 正确
int value = matrix[1][2];  // 7

// 错误
int value = matrix[1][5];  // 越界
int value = matrix[3][0];  // 越界

用std::array或嵌套的std::vector更清晰:

#include <array>
#include <vector>

std::array<std::array<int, 4>, 3> matrix;

// 或
std::vector<std::vector<int>> matrix(3, std::vector<int>(4));

数组可以用负数索引(虽然不常见):

int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr + 3;
std::cout << ptr[-1] << std::endl;  // 访问arr[2],这是合法的

// 但直接这样不行
std::cout << arr[-1] << std::endl;  // 未定义行为

如何避免?有几个方法:

  1. 使用STL容器(std::vector、std::array等)
  2. 使用范围for循环和迭代器
  3. 边界检查:如果需要边界检查,使用at()而不是[]

一个安全的访问函数:

template<typename Container>
auto safe_at(const Container& container, size_t index) {
    if (index >= container.size()) {
        throw std::out_of_range("Index out of range");
    }
    return container[index];
}

当然编译器和工具也可以协助:

# GCC/Clang的地址消毒器
g++ -fsanitize=address -g your_code.cpp

# 运行时检测数组越界

或者用Valgrind:

valgrind ./your_program

写在最后

数组越界是C++不检查边界带来的副作用。最好的防御是:

  • 优先使用STL容器
  • 使用范围for循环和迭代器
  • 仔细检查边界条件
  • 使用工具检测

编译器信任你知道自己在做什么。如果你不确定,就用更安全的工具。在C++里,安全不是默认的,需要你自己去争取。用STL容器,用范围for循环,让代码更安全、更清晰。

您在使用c++的过程中这个问题或者类似的其他”坑”吗,欢迎在留言区分享讨论,也欢迎关注收藏,我们将持续分享更多好文,多谢.

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...