admin管理员组

文章数量:1130349

实验项目:任务2:

在任务1基础上改写 SecureBuffer 类,模拟以下几种设计缺陷与错误操作,使用 Valgrind/AddressSanitizer或其他代码检测工具检测问题:

拷贝构造

  1. 为 SecureBuffer 类添加拷贝构造函数:
    1. 浅拷贝:直接拷贝指针
    2. 深拷贝:重新分配内存并复制数据

在 main() 中通过对象赋值或拷贝构造,展示浅拷贝导致的问题(修改浅拷贝,影响原对象)

悬空指针

  1. 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果

(如果在任务一种已完成这一步的模拟,可跳过这一步,实验报告中写上“已在任务一完成”)

重复释放

  1. 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果

内存泄漏

  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示

提交:

1. 代码(c++)

2. 测试用例

3. 过程及结果截图

4. 文字分析(依任务要求)

目录

实验项目:任务2:

实验步骤、实验结果及结果分析: 

1)拷贝构造

修改思路

测试用例

 过程及结果截图

文字分析

2)悬空指针

修改思路

测试用例

过程及结果截图

文字分析

3)重复释放

修改思路

测试用例

过程及结果截图

文字分析

4)内存泄漏

修改思路

测试用例

过程及结果截图

文字分析

HEAP SUMMARY

Definitely Lost

LEAK SUMMARY

ERROR SUMMARY


 

实验步骤、实验结果及结果分析: 

// 原始代码
#include<iostream>
#include<cstring>
class SecureBuffer{
	private:
		char* m_data;
		size_t m_size;
	public:
		SecureBuffer(size_t size):m_size(size){
			m_data=new char[m_size];
			memset(m_data,0xCC,m_size);
		}
		~SecureBuffer(){
			memset(m_data,0x00,m_size);
			delete[] m_data;
		}
	void print_data(){
		for(size_t i=0;i<m_size;++i){
			std::cout<<std::hex<<(int)m_data[i]<<" ";
		}
		std::cout<<std::endl;
	}
	char* get_data(){
		return m_data;
	}
		
};

int main(){
	// 1
	SecureBuffer* buffer=new SecureBuffer(10);
	std::cout << "After construction: ";
	buffer->print_data();
	buffer->~SecureBuffer();
	std::cout << "After explicit destruction: ";
	buffer->print_data();
	// 2
	std::cout << "Trying to read after destruction: ";
	std::cout<<std::hex<<(int)buffer->get_data()[0]<<std::endl;
	std::cout << "Trying to modify after destruction: ";
	buffer->get_data()[0]=0xFF;
	buffer->print_data();
	// 3
	operator delete(buffer);
	std::cout << "After operator delete: ";
	std::cout << std::hex << (int)buffer->get_data()[0] << std::endl;	
}

 


1)拷贝构造

  1. 为 SecureBuffer 类添加拷贝构造函数:
    1. 浅拷贝:直接拷贝指针
    2. 深拷贝:重新分配内存并复制数据
修改思路
  1. 浅拷贝

    • 在拷贝构造函数中直接复制指针,使新对象和原对象共享同一块内存。
    • 修改新对象的数据时,原对象的数据也会被修改。
  2. 深拷贝

    • 在拷贝构造函数中重新分配内存,并复制原对象的数据。
    • 新对象和原对象拥有独立的内存,修改新对象不会影响原对象。
// 构造函数
    SecureBuffer(size_t size) : m_size(size) {
        m_data = new char[m_size];
        memset(m_data, 0xCC, m_size);
    }
    // 浅拷贝构造函数
    SecureBuffer(const SecureBuffer& other) : m_size(other.m_size) {
        m_data = other.m_data; // 直接复制指针
    }
    // 深拷贝构造函数
    SecureBuffer(const SecureBuffer& other, bool deepCopy) : m_size(other.m_size) {
        if (deepCopy) {
            m_data = new char[m_size];
            memcpy(m_data, other.m_data, m_size);
        } else {
            m_data = other.m_data; // 直接复制指针
        }
    }
测试用例
    // 设置数据
    void set_data(size_t index, char value) {
        if (index < m_size) {
            m_data[index] = value;
        }
  1. set_data 方法:添加了 set_data 方法,用于修改 m_data 中的内容。
  2. 深拷贝构造函数的调用:在 main 函数中,显式调用了带有 deepCopy 参数的构造函数来进行深拷贝。
int main() {
    SecureBuffer buffer1(10);
    buffer1.set_data(0, 'A');
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    // 浅拷贝
    SecureBuffer buffer2 = buffer1; // 使用拷贝构造函数
    buffer2.set_data(0, 'X'); // 修改 buffer2 的数据
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    std::cout << "Buffer2 data: " << buffer2.get_data()[0] << std::endl; 
    // 深拷贝
    SecureBuffer buffer3(buffer1, true); // 使用深拷贝构造函数
    buffer3.set_data(0, 'Y'); // 修改 buffer3 的数据
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    std::cout << "Buffer3 data: " << buffer3.get_data()[0] << std::endl;
    return 0;   
}
 过程及结果截图

文字分析

浅拷贝共享内存,而深拷贝则独立分配内存并复制数据。


2)悬空指针

  1. 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果 
修改思路
  1. 使用 new 创建一个 SecureBuffer 对象。
  2. 显式调用析构函数销毁对象,但不置空指针。
  3. 通过该指针访问成员函数,观察悬空指针带来的后果。
    ~SecureBuffer() {
        if (m_data) {
            std::memset(m_data, 0x00, m_size);  // 清空内存为 0x00
            delete[] m_data;
            // m_data = nullptr;  // 将指针置为 nullptr,避免悬空指针
        }
    }
int main() {
    SecureBuffer* buffer3 = new SecureBuffer(10);
    buffer3->~SecureBuffer(); 
    buffer3->print_data(); 
    return 0;
}
测试用例
int main() {
    // 悬空指针测试
    SecureBuffer* buffer3 = new SecureBuffer(10);
    buffer3->print_data(); // 正常输出数据
    buffer3->~SecureBuffer(); // 显式调用析构函数
    buffer3->print_data(); // 访问悬空指针,可能导致崩溃或输出随机值
    return 0;   
}
过程及结果截图

文字分析

访问悬空指针会导致未定义行为,可能引发程序崩溃或输出随机值。为了避免悬空指针问题,应该在释放内存后将指针置为 nullptr,并在访问指针前检查其是否为 nullptr


3)重复释放

  1. 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
修改思路
  1. 使用 new 创建一个 SecureBuffer 对象。
  2. 调用 operator delete 释放内存。
  3. 再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为。
int main() {
    SecureBuffer* buffer4 = new SecureBuffer(10);
    delete buffer4; 
    // delete buffer4; // 取消注释以模拟重复释放
    return 0;
}
测试用例
int main() {
    // 重复释放测试
    SecureBuffer* buffer4 = new SecureBuffer(10);
    delete buffer4; // 第一次释放
    delete buffer4; // 第二次释放,模拟重复释放
    return 0;   
}
过程及结果截图

文字分析

重复释放会导致程序崩溃,会导致未定义行为,操作系统可能会检测到内存错误并终止程序。为了避免重复释放问题,建议在释放内存后将指针置为 nullptr,并在释放前检查指针是否为 nullptr。此外,使用智能指针(如 std::unique_ptr 或 std::shared_ptr)可以自动管理内存,避免手动释放内存带来的风险。


4)内存泄漏

  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
修改思路
  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象。
  2. 不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。
  3. 使用 Valgrind 或 AddressSanitizer 检测内存泄漏。
int main() {
    for (int i = 0; i < 10000; ++i) {
        SecureBuffer* tempBuffer = new SecureBuffer(10);
    }
    std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
    return 0;
}

使用 Valgrind 检测内存问题: 

sudo apt-get install valgrind   
g++ -g 2.cpp -o 2
valgrind --leak-check=full ./2
测试用例
int main() {
    // 内存泄漏测试
    for (int i = 0; i < 10000; ++i) {
        SecureBuffer* tempBuffer = new SecureBuffer(10);
        // 不释放内存
    }
    std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
    return 0;   
}
过程及结果截图

 

文字分析
HEAP SUMMARY
==2907==     in use at exit: 260,000 bytes in 20,000 blocks   
==2907==   total heap usage: 20,002 allocs, 2 frees, 333,728 bytes allocated
  • in use at exit:程序退出时,仍有 260,000 字节的内存未被释放。
  • total heap usage:程序总共进行了 20,002 次内存分配,但只释放了 2 次,导致大量内存未被释放。

Definitely Lost
==2907== 260,000 (160,000 direct, 100,000 indirect) bytes in 10,000 blocks are definitely lost in loss record 2 of 2   
==2907==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)   
==2907==    by 0x108B0E: main (in /home/liao/1)
  • definitely lost:明确表示有 260,000 字节的内存泄漏。
    • 160,000 direct:直接泄漏的内存为 160,000 字节。
    • 100,000 indirect:间接泄漏的内存为 100,000 字节。
  • 泄漏位置:内存泄漏发生在 main 函数中,具体是在调用 operator new 分配内存时。

LEAK SUMMARY
==2907==    definitely lost: 160,000 bytes in 10,000 blocks   
==2907==    indirectly lost: 100,000 bytes in 10,000 blocks   
==2907==      possibly lost: 0 bytes in 0 blocks   
==2907==    still reachable: 0 bytes in 0 blocks   
==2907==         suppressed: 0 bytes in 0 blocks
  • definitely lost:明确泄漏的内存为 160,000 字节,分布在 10,000 个块中。
  • indirectly lost:间接泄漏的内存为 100,000 字节,分布在 10,000 个块中。
  • possibly lost:没有可能泄漏的内存。
  • still reachable:没有仍然可访问的内存。
  • suppressed:没有被抑制的错误。

ERROR SUMMARY
==2907== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  • ERROR SUMMARY:总共检测到 1 个错误,来自 1 个上下文。

实验项目:任务2:

在任务1基础上改写 SecureBuffer 类,模拟以下几种设计缺陷与错误操作,使用 Valgrind/AddressSanitizer或其他代码检测工具检测问题:

拷贝构造

  1. 为 SecureBuffer 类添加拷贝构造函数:
    1. 浅拷贝:直接拷贝指针
    2. 深拷贝:重新分配内存并复制数据

在 main() 中通过对象赋值或拷贝构造,展示浅拷贝导致的问题(修改浅拷贝,影响原对象)

悬空指针

  1. 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果

(如果在任务一种已完成这一步的模拟,可跳过这一步,实验报告中写上“已在任务一完成”)

重复释放

  1. 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果

内存泄漏

  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示

提交:

1. 代码(c++)

2. 测试用例

3. 过程及结果截图

4. 文字分析(依任务要求)

目录

实验项目:任务2:

实验步骤、实验结果及结果分析: 

1)拷贝构造

修改思路

测试用例

 过程及结果截图

文字分析

2)悬空指针

修改思路

测试用例

过程及结果截图

文字分析

3)重复释放

修改思路

测试用例

过程及结果截图

文字分析

4)内存泄漏

修改思路

测试用例

过程及结果截图

文字分析

HEAP SUMMARY

Definitely Lost

LEAK SUMMARY

ERROR SUMMARY


 

实验步骤、实验结果及结果分析: 

// 原始代码
#include<iostream>
#include<cstring>
class SecureBuffer{
	private:
		char* m_data;
		size_t m_size;
	public:
		SecureBuffer(size_t size):m_size(size){
			m_data=new char[m_size];
			memset(m_data,0xCC,m_size);
		}
		~SecureBuffer(){
			memset(m_data,0x00,m_size);
			delete[] m_data;
		}
	void print_data(){
		for(size_t i=0;i<m_size;++i){
			std::cout<<std::hex<<(int)m_data[i]<<" ";
		}
		std::cout<<std::endl;
	}
	char* get_data(){
		return m_data;
	}
		
};

int main(){
	// 1
	SecureBuffer* buffer=new SecureBuffer(10);
	std::cout << "After construction: ";
	buffer->print_data();
	buffer->~SecureBuffer();
	std::cout << "After explicit destruction: ";
	buffer->print_data();
	// 2
	std::cout << "Trying to read after destruction: ";
	std::cout<<std::hex<<(int)buffer->get_data()[0]<<std::endl;
	std::cout << "Trying to modify after destruction: ";
	buffer->get_data()[0]=0xFF;
	buffer->print_data();
	// 3
	operator delete(buffer);
	std::cout << "After operator delete: ";
	std::cout << std::hex << (int)buffer->get_data()[0] << std::endl;	
}

 


1)拷贝构造

  1. 为 SecureBuffer 类添加拷贝构造函数:
    1. 浅拷贝:直接拷贝指针
    2. 深拷贝:重新分配内存并复制数据
修改思路
  1. 浅拷贝

    • 在拷贝构造函数中直接复制指针,使新对象和原对象共享同一块内存。
    • 修改新对象的数据时,原对象的数据也会被修改。
  2. 深拷贝

    • 在拷贝构造函数中重新分配内存,并复制原对象的数据。
    • 新对象和原对象拥有独立的内存,修改新对象不会影响原对象。
// 构造函数
    SecureBuffer(size_t size) : m_size(size) {
        m_data = new char[m_size];
        memset(m_data, 0xCC, m_size);
    }
    // 浅拷贝构造函数
    SecureBuffer(const SecureBuffer& other) : m_size(other.m_size) {
        m_data = other.m_data; // 直接复制指针
    }
    // 深拷贝构造函数
    SecureBuffer(const SecureBuffer& other, bool deepCopy) : m_size(other.m_size) {
        if (deepCopy) {
            m_data = new char[m_size];
            memcpy(m_data, other.m_data, m_size);
        } else {
            m_data = other.m_data; // 直接复制指针
        }
    }
测试用例
    // 设置数据
    void set_data(size_t index, char value) {
        if (index < m_size) {
            m_data[index] = value;
        }
  1. set_data 方法:添加了 set_data 方法,用于修改 m_data 中的内容。
  2. 深拷贝构造函数的调用:在 main 函数中,显式调用了带有 deepCopy 参数的构造函数来进行深拷贝。
int main() {
    SecureBuffer buffer1(10);
    buffer1.set_data(0, 'A');
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    // 浅拷贝
    SecureBuffer buffer2 = buffer1; // 使用拷贝构造函数
    buffer2.set_data(0, 'X'); // 修改 buffer2 的数据
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    std::cout << "Buffer2 data: " << buffer2.get_data()[0] << std::endl; 
    // 深拷贝
    SecureBuffer buffer3(buffer1, true); // 使用深拷贝构造函数
    buffer3.set_data(0, 'Y'); // 修改 buffer3 的数据
    std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl; 
    std::cout << "Buffer3 data: " << buffer3.get_data()[0] << std::endl;
    return 0;   
}
 过程及结果截图

文字分析

浅拷贝共享内存,而深拷贝则独立分配内存并复制数据。


2)悬空指针

  1. 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果 
修改思路
  1. 使用 new 创建一个 SecureBuffer 对象。
  2. 显式调用析构函数销毁对象,但不置空指针。
  3. 通过该指针访问成员函数,观察悬空指针带来的后果。
    ~SecureBuffer() {
        if (m_data) {
            std::memset(m_data, 0x00, m_size);  // 清空内存为 0x00
            delete[] m_data;
            // m_data = nullptr;  // 将指针置为 nullptr,避免悬空指针
        }
    }
int main() {
    SecureBuffer* buffer3 = new SecureBuffer(10);
    buffer3->~SecureBuffer(); 
    buffer3->print_data(); 
    return 0;
}
测试用例
int main() {
    // 悬空指针测试
    SecureBuffer* buffer3 = new SecureBuffer(10);
    buffer3->print_data(); // 正常输出数据
    buffer3->~SecureBuffer(); // 显式调用析构函数
    buffer3->print_data(); // 访问悬空指针,可能导致崩溃或输出随机值
    return 0;   
}
过程及结果截图

文字分析

访问悬空指针会导致未定义行为,可能引发程序崩溃或输出随机值。为了避免悬空指针问题,应该在释放内存后将指针置为 nullptr,并在访问指针前检查其是否为 nullptr


3)重复释放

  1. 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
修改思路
  1. 使用 new 创建一个 SecureBuffer 对象。
  2. 调用 operator delete 释放内存。
  3. 再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为。
int main() {
    SecureBuffer* buffer4 = new SecureBuffer(10);
    delete buffer4; 
    // delete buffer4; // 取消注释以模拟重复释放
    return 0;
}
测试用例
int main() {
    // 重复释放测试
    SecureBuffer* buffer4 = new SecureBuffer(10);
    delete buffer4; // 第一次释放
    delete buffer4; // 第二次释放,模拟重复释放
    return 0;   
}
过程及结果截图

文字分析

重复释放会导致程序崩溃,会导致未定义行为,操作系统可能会检测到内存错误并终止程序。为了避免重复释放问题,建议在释放内存后将指针置为 nullptr,并在释放前检查指针是否为 nullptr。此外,使用智能指针(如 std::unique_ptr 或 std::shared_ptr)可以自动管理内存,避免手动释放内存带来的风险。


4)内存泄漏

  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
修改思路
  1. 在 main() 中批量创建 10000 个 SecureBuffer 临时对象。
  2. 不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。
  3. 使用 Valgrind 或 AddressSanitizer 检测内存泄漏。
int main() {
    for (int i = 0; i < 10000; ++i) {
        SecureBuffer* tempBuffer = new SecureBuffer(10);
    }
    std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
    return 0;
}

使用 Valgrind 检测内存问题: 

sudo apt-get install valgrind   
g++ -g 2.cpp -o 2
valgrind --leak-check=full ./2
测试用例
int main() {
    // 内存泄漏测试
    for (int i = 0; i < 10000; ++i) {
        SecureBuffer* tempBuffer = new SecureBuffer(10);
        // 不释放内存
    }
    std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
    return 0;   
}
过程及结果截图

 

文字分析
HEAP SUMMARY
==2907==     in use at exit: 260,000 bytes in 20,000 blocks   
==2907==   total heap usage: 20,002 allocs, 2 frees, 333,728 bytes allocated
  • in use at exit:程序退出时,仍有 260,000 字节的内存未被释放。
  • total heap usage:程序总共进行了 20,002 次内存分配,但只释放了 2 次,导致大量内存未被释放。

Definitely Lost
==2907== 260,000 (160,000 direct, 100,000 indirect) bytes in 10,000 blocks are definitely lost in loss record 2 of 2   
==2907==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)   
==2907==    by 0x108B0E: main (in /home/liao/1)
  • definitely lost:明确表示有 260,000 字节的内存泄漏。
    • 160,000 direct:直接泄漏的内存为 160,000 字节。
    • 100,000 indirect:间接泄漏的内存为 100,000 字节。
  • 泄漏位置:内存泄漏发生在 main 函数中,具体是在调用 operator new 分配内存时。

LEAK SUMMARY
==2907==    definitely lost: 160,000 bytes in 10,000 blocks   
==2907==    indirectly lost: 100,000 bytes in 10,000 blocks   
==2907==      possibly lost: 0 bytes in 0 blocks   
==2907==    still reachable: 0 bytes in 0 blocks   
==2907==         suppressed: 0 bytes in 0 blocks
  • definitely lost:明确泄漏的内存为 160,000 字节,分布在 10,000 个块中。
  • indirectly lost:间接泄漏的内存为 100,000 字节,分布在 10,000 个块中。
  • possibly lost:没有可能泄漏的内存。
  • still reachable:没有仍然可访问的内存。
  • suppressed:没有被抑制的错误。

ERROR SUMMARY
==2907== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  • ERROR SUMMARY:总共检测到 1 个错误,来自 1 个上下文。

本文标签: 内存课程动态