2024.12.26 os lab3

2024.12.26 os lab3

原代码

地址:https://github.com/BUPT-OS/easy_lab/tree/lab3

运行未修改的代码,并且注释掉cout时发生错误:

malloc(): corrupted top size

如果不注释cout,可以正常运行

image-20241226105708712

1.不注释 cout 时堆内存的详细分析

1. 程序启动阶段

  • 在程序启动时,堆的初始状态为空,堆顶指针位于堆的起始位置。
  • 当程序第一次需要在堆上分配内存时,堆顶会向上增长(通常从低地址向高地址)。

2. 第一处堆分配:cout 调用

cout << "A magic print! If you comment this, the program will break." << endl;

cout被调用时,C++ 标准库会在堆上分配一个缓冲区,用于存储待输出的字符串内容。

缓冲区大小:在常见的实现中(如 GNU libstdc++),这个缓冲区的默认大小为 1024 字节

分配位置:此时缓冲区位于堆的最底部,是堆中的第一个分配块。

堆布局(堆顶向高地址增长)

+-------------------+<-- 堆底
| cout 缓冲区 (1024) |
+-------------------+

3. 第二处堆分配:double_array 函数调用

int **result = new int*[8];

在 double_array函数中,result 被分配在堆上。它是一个指针数组,用于存储指向 matrix 中行的指针。

分配大小8 * sizeof(int*),假设指针大小为 8 字节,则总大小为 64 字节

分配位置:由于堆是按顺序分配的,result 的内存块紧跟在 cout 缓冲区之后。

堆布局

+-------------------+<-- 堆底
| cout 缓冲区 (1024) |
+-------------------+
| result (64)        |
+-------------------+

4. 越界访问的堆内存情况

double_array 函数中,存在以下越界问题:

for (int i = 0; i < n; ++i) {
    result[i] = matrix[i];
}

result 仅分配了 8 个指针的空间(new int*[8]),但代码试图访问 result[0]result[63](共 64 个)。越界访问后,result的内容会覆盖 cout缓冲区的部分内存:result[8]result[63] 的内容会覆盖 cout 缓冲区中第 64 字节后的数据。因为 cout 缓冲区仍是已分配的内存,访问它不会触发段错误。


5. 第三处堆分配:在主函数中的 matrix 静态分配

int matrix[array_number][array_number];

matrix 是一个静态数组,分配在全局内存区域(通常位于数据段)。由于 matrix 的内存地址在堆之外,result[i] = matrix[i]; 操作不会触发段错误,即使 result 越界。


6. 输出数据阶段

在主函数中,遍历 result 并输出数据:

for (int i = 0; i < array_number; ++i) {
    cout << "print address of result[" << i << "] " << &result[i][0] << endl;
    for (int j = 0; j < array_number; j++) {
        result[i][j] = j;
        cout << "print content of result[" << i << "][" << j << "] " << result[i][j] << endl;
    }
}

cout 输出时,cout 缓冲区的内容可能已被越界的 result 写入所覆盖,但这不会立即引发错误。覆盖的内容可能只是错误输出,但不会导致程序崩溃。


堆内存最终布局(带越界覆盖的情况)

  1. 初始分配:cout 缓冲区

    +-------------------+  <-- 堆底
    | cout 缓冲区 (1024) |
    +-------------------+
    
  2. 分配 result 并发生越界覆盖

    +-------------------+  <-- 堆底
    | result (覆盖部分)  |
    | 未被访问部分       |  <-- result[8] 开始覆盖
    +-------------------+
    

由于越界覆盖未触及未分配的非法内存(仍在已分配的 cout 缓冲区范围内),程序避免了段错误。


2. 原代码中导致错误的原因

2.1 堆内存分配不足

int **result = new int*[8];

​ 分配的 result 指针数组大小仅为 8,但程序中访问了 result[0]result[63],显然越界。如果没有 cout 创建的缓冲区,result 的越界可能直接访问未分配或非法的内存区域,导致段错误或堆损坏。

2.2 注释掉 cout 导致未定义行为

cout 的第一处调用被注释掉后,堆上不再创建缓冲区,导致 result 的越界访问破坏了堆的关键结构。如果保留 cout,堆的分配顺序发生了变化,result 的越界行为可能恰好访问到 cout 的缓冲区或未使用的内存区域,从而掩盖了潜在的错误。


3. 解决问题的根本方法

原代码依赖 cout 来掩盖问题是极其不可靠的,正确的方法应当是修复代码中的根本性错误:

正确分配内存大小

int **result = new int*[n]; // n = 64

避免堆内存越界: 确保对指针数组的访问范围在合法区域内。

解决方案

调整内存分配大小: 确保 result 指针数组的大小与实际访问范围一致:

int **result = new int*[n];
#include <iostream>

using namespace std;

#define array_number 64

int matrix[array_number][array_number];

int **double_array(size_t n) {
    // 修复: 确保分配大小为 n
    int **result = new int*[n]; 

    for (int i = 0; i < n; ++i) {
        result[i] = matrix[i]; // 浅拷贝,指向 matrix[i]
        for (int j = 0; j < n; ++j) {
            result[i][j] = j; // 初始化 matrix[i][j]
        }
    }

    return result;
}

int main() {
    // 修复: 注释掉 cout 语句不再影响执行
    // cout << "A magic print! If you comment this, the program will break." << endl;
    int **result = double_array(array_number);

    for (int i = 0; i < array_number; ++i) {
        cout << "print address of result[" << i << "] " << result[i] << endl;
        for (int j = 0; j < array_number; ++j) {
            cout << "print content of result[" << i << "][" << j << "] " << result[i][j] << endl;
        }
    }

    // 修复: 仅释放动态分配的 result 数组
    delete[] result;

    return 0;
}

代码说明

  1. 动态内存分配调整

    int **result = new int*[n];
    

    分配大小为 n,避免越界访问。


结果与总结

修复后程序正常运行,解决了以下问题:避免越界访问:通过正确分配内存,确保访问合法。通过此次修复,理解了内存管理的重要性。

posted @ 2024-12-26 11:30  vast_joy  阅读(3)  评论(0编辑  收藏  举报