2024.12.26 os lab3

news/2024/12/27 15:14:31/文章来源:https://www.cnblogs.com/vastjoy/p/18632354

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 64int matrix[array_number][array_number];int **double_array(size_t n) {// 修复: 确保分配大小为 nint **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,避免越界访问。


结果与总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/859247.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编译型语言的痛!但无文件落地我可以!!!

再不进来看,哭了我就不管你了!!!声明:本文主要用作技术分享,所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险,并遵循相关法律法规。 @目录python 等脚本语言示例演示golang 等静态编译型语言内存文件系统memfd_create 使用…

WPF TabControl 去掉鼠标悬浮效果

1.资源<Window.Resources><Style x:Key="TabStyle" TargetType="TabItem"><Setter Property="TextBlock.FontSize" Value="12" /><Setter Property="Template"><Setter.Value><ControlTem…

Clion创建项目sqlite数据库

1.创建工程并运行 (1)新建——project,选择工程文件夹,创建工程(2)进入界面,等待项目启动,显示main.cpp主文件(3)配置编译环境默认生成了main.cpp文件,但是我们不能运行。我们缺少编译环境,点击Setting打开设置,搜索工具链,发现Clion提供的编译环境有几种MinGW,Cy…

营销相关笔记2:电力营销的业务办理和计量计费

电力营销的业务办理和计量计费 转:电力知识图谱:电力营销(下) - 知乎 一.业务办理 电力营销是和客户打交道的最前线,两大业务:一是市场销售,二是客户需求受理和交付,即业务办理。在一些市场化主导的售电公司里,市场部和营销部可能会分开。但在电网公司,都是统一的营销…

FMC子卡设计原理图:FMC209-基于FMC的4路125MAD输入、2路1GDA输出子卡 中低频信号采集

一、板卡概述本子卡基于FMC连接器实现4路125M采样率AD输出,两路1G采样率DA输出子卡,板卡默认由FMC连接器+12V供电,支持外参考时钟,外输入时钟,外触发。 二、性能指标三、应用领域 中低频信号采集

【安全就业】2024年网络安全技术技能人才职业能力图谱+电子数据取证

电子数据取证,是指利用科学和法律方法对电子设备中的数据进行搜集、分析、保存和报告的过程,以确保这些信息在法律程序中能够作为线索或证据使用。电子数据取证工作,一般需要取证人员具备电子数据提取、电子数据恢复、数据库系统取证、电子数据治理、程序功能分析、现场勘查…

HuntBack(反击狩猎):用于攻防演练中,防守方对恶意ip进行web指纹扫描与识别

#溯源 #攻防演练 HuntBack(反击狩猎),用于攻防演练中,防守方对恶意ip进行web指纹扫描与识别 应用场景 在蓝队职守中,安全设备爆出恶意攻击ip地址,如果对方使用的是自己的服务器,并且搭建了一些安全业务,可使用本工具对目前已知工具进行探测。 功能 1.红队指纹识别 2.ipwh…

代码随想录——贪心23监控二叉树

思路 这道题目首先要想,如何放置,才能让摄像头最小的呢? 从题目中示例,其实可以得到启发,我们发现题目示例中的摄像头都没有放在叶子节点上! 这是很重要的一个线索,摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。 所以把摄像头放在叶子节…

CH585 CH584 I2C时钟配置(超1MHz 最高1.8MHz)

I2C的R16_I2C_CTRL2寄存器描述:[5:0] FREQ :允许的范围在2~36MHz之间。必须设置在000010b 到100100b 之间RB_I2C_FREQ决定SCL的建立时间和SDA的保持时间,理论上I2C 频率可以达到一分频,实际频率可在36MHz之上,可使用最高80MHz。 I2C的R16_I2C_CKCFGR寄存器描述:[11:0] CC…

JMeter JDBC 请求实战宝典

《JMeter JDBC 请求实战宝典》 宝子们,今天咱就来唠唠 JMeter 里超厉害的 JDBC 请求,这玩意儿就像是数据库世界的神奇魔杖,能帮咱把数据库里的各种秘密(数据)都挖出来,还能对这些数据进行各种操作,不管是查查看、改一改,还是加点新东西、删点旧东西,它都能轻松搞定,而…

STM32-I2C软件模拟

1.I2C介绍 I2C是一种多主机、两线制、低速串行通信总线,广泛用于微控制器和各种外围设备之间的通信。它使用两条线路:串行数据线(SDA)和串行时钟线(SCL)进行双向传输。2.时序启动条件:SCL高电平时、SDA由高电平变为低电平 停止条件:SCL高电平时、SDA由低电平变为高电平…

系统攻防-WindowsLinux远程探针本地自检任意代码执行权限提升入口点

Windows&Linux&远程探针&本地自检&任意代码执行&权限提升&入口点知识点: 1、远程漏扫-Nessus&Nexpose&Goby 2、本地漏扫(提权)-Wesng&Tiquan&Suggester 3、利用场景-远程利用&本地利用&利用条件一、演示案例-操作系统-远程漏…