【C/C++】——小白初步了解——内存管理
CSDN 2024-06-18 16:05:19 阅读 92
目录
1. C/C++内存分布
代码区(Code Segment):
数据区(Data Segment):
堆区(Heap):
栈区(Stack):
常量区(Constant Segment):
2. C语言中动态内存管理方式
1.malloc(size_t size):
2.calloc(size_t nmemb, size_t size):
3.*realloc(void ptr, size_t size):
4.*free(void ptr):
3. C++中动态内存管理
1.new:
2.delete:
4. operator new与operator delete函数
5. new和delete的实现原理
6. 定位new表达式(placement-new)
7. 常见面试题
1.解释C++中new和malloc的区别
2.什么是内存泄漏?如何避免?
3.解释C++中的RAII(Resource Acquisition Is Initialization)
4.解释栈区和堆区的区别
5.如何实现自己的内存池?
1. C/C++内存分布
一个典型的C/C++程序在内存中的布局如下:
代码区(Code Segment):
存储程序的机器指令,由编译器生成。该区域通常是只读的,以防止程序在运行时修改自身的指令。代码区在程序加载时被操作系统加载到内存中。数据区(Data Segment):
存储全局变量和静态变量,包括已初始化和未初始化的变量。数据区又分为两部分: 已初始化数据区(Initialized Data Segment): 存储程序中已初始化的全局变量和静态变量。未初始化数据区(Uninitialized Data Segment or BSS): 存储未初始化的全局变量和静态变量,程序启动时这些变量会被初始化为0。堆区(Heap):
用于动态内存分配,大小不固定,可以在程序运行时动态地增长或缩小。由程序员手动管理内存的分配和释放。常用的函数有malloc()
和 free()
。堆区的内存分配效率较低,但灵活性高。 栈区(Stack):
用于函数调用时的临时存储,包括函数的局部变量、参数和返回地址。栈区的内存由编译器自动分配和释放,具有后进先出的特点。栈区内存分配效率高,但大小有限,通常由操作系统决定。常量区(Constant Segment):
存储常量数据,如字符串字面量和常量变量。通常也是只读的,以保护常量数据不被修改。2. C语言中动态内存管理方式
在C语言中,动态内存管理主要通过以下几个函数实现:
1.malloc(size_t size):
功能:分配指定大小的字节,并返回一个指向这块内存的指针。
特点:分配的内存未初始化,内容是随机的。
#include <stdio.h>#include <stdlib.h>int main() { int *arr = (int *)malloc(10 * sizeof(int)); if (arr == NULL) { printf("Memory allocation failed\n"); return 1; } for (int i = 0; i < 10; i++) { arr[i] = i; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } free(arr); return 0;}
2.calloc(size_t nmemb, size_t size):
功能:分配nmemb个元素,每个元素size字节,并初始化所有分配的字节为0。
特点:分配的内存被初始化为0,适合分配需要清零的数组。
#include <stdio.h>#include <stdlib.h>int main() { int *arr = (int *)calloc(10, sizeof(int)); if (arr == NULL) { printf("Memory allocation failed\n"); return 1; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } free(arr); return 0;}
3.*realloc(void ptr, size_t size):
功能:调整之前分配的内存块的大小。
特点:如果新大小大于原大小,新分配的内存区域中的内容是不确定的;如果新大小小于原大小,超出的内容将被丢弃。
#include <stdio.h>#include <stdlib.h>int main() { int *arr = (int *)malloc(5 * sizeof(int)); if (arr == NULL) { printf("Memory allocation failed\n"); return 1; } for (int i = 0; i < 5; i++) { arr[i] = i; } arr = (int *)realloc(arr, 10 * sizeof(int)); if (arr == NULL) { printf("Memory allocation failed\n"); return 1; } for (int i = 5; i < 10; i++) { arr[i] = i; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } free(arr); return 0;}
4.*free(void ptr):
功能:释放之前分配的内存块,使其可以重新分配。
特点:释放后,指针ptr不再指向有效的内存区域,应该将ptr置为NULL以防止野指针错误。
#include <stdio.h>#include <stdlib.h>int main() { int *arr = (int *)malloc(10 * sizeof(int)); if (arr == NULL) { printf("Memory allocation failed\n"); return 1; } free(arr); arr = NULL; return 0;}
3. C++中动态内存管理
在C++中,动态内存管理不仅可以使用C语言的函数(如malloc、calloc等),还提供了更高级的 new
和 delete
运算符:
1.new:
功能:分配指定类型的内存,并调用该类型的构造函数。
示例代码:
#include <iostream>int main() { int *arr = new int[10]; for (int i = 0; i < 10; i++) { arr[i] = i; } for (int i = 0; i < 10; i++) { std::cout << arr[i] << " "; } delete[] arr; return 0;}
2.delete:
功能:释放用new分配的内存,并调用该类型的析构函数。#include <iostream>int main() { int *arr = new int[10]; delete[] arr; return 0;}
在C++中,使用 new
和 delete
操作符进行内存管理比使用C语言中的函数更方便,因为它们不仅分配和释放内存,还自动调用构造函数和析构函数,确保对象在创建和销毁时执行必要的初始化和清理工作。
4. operator new与operator delete函数
C++中,operator new
和 operator delete
是为对象分配和释放内存的函数。它们类似于 malloc
和 free
,但有一些重要区别:
operator new:
功能:分配指定大小的内存,但不调用构造函数。
通常在类的new运算符中隐式调用。
#include <iostream>void* operator new(size_t size) { std::cout << "Custom new for size " << size << std::endl; return malloc(size);}void operator delete(void* ptr) noexcept { std::cout << "Custom delete" << std::endl; free(ptr);}int main() { int *p = new int(10); delete p; return 0;}
operator delete:
功能:释放用 operator new
分配的内存,但不调用析构函数。通常在类的delete运算符中隐式调用。
可以重载这两个函数以定制内存分配行为。例如,在需要跟踪内存分配和释放的场景中,可以重载 operator new
和 operator delete
以记录每次内存操作的日志。
5. new和delete的实现原理
new
和 delete
的实现可以分为两个步骤:
new:
调用 operator new
分配内存。在分配的内存上调用构造函数初始化对象。
delete:
在内存上调用析构函数销毁对象。调用 operator delete
释放内存。
示例代码展示了new和delete的工作机制:
#include <iostream>class MyClass {public: MyClass() { std::cout << "Constructor called" << std::endl; } ~MyClass() { std::cout << "Destructor called" << std::endl; }};int main() { MyClass *obj = new MyClass(); delete obj; return 0;}
在上面的代码中,当我们使用 new MyClass()
创建对象时,首先调用 operator new
分配内存,然后在分配的内存上调用 MyClass
的构造函数。当我们使用 delete obj
删除对象时,首先调用 MyClass
的析构函数,然后调用 operator delete
释放内存。
6. 定位new表达式(placement-new)
placement new
是C++中的一个高级特性,用于在已分配的内存上构造对象。它不会分配新的内存,只是调用对象的构造函数。
#include <iostream>int main() { char buffer[sizeof(int)]; int *p = new (buffer) int(5); // 在buffer中构造int std::cout << *p << std::endl; p->~int(); // 手动调用析构函数 return 0;}
在上面的代码中,我们在预先分配的内存 buffer
中使用 placement new
构造了一个 int
对象。这种技术通常用于自定义内存池或优化程序性能。
7. 常见面试题
1.解释C++中new和malloc的区别
new: 分配内存并调用构造函数初始化对象。返回对象的指针。可以重载。用于分配类对象。malloc: 仅分配内存,不调用构造函数。返回void*
类型的指针,需要类型转换。不能重载。用于分配任意类型的内存。 2.什么是内存泄漏?如何避免?
内存泄漏: 是指程序在分配内存后,未能正确释放已分配的内存,导致内存无法被重新利用。避免方法: 使用智能指针(如std::unique_ptr
和 std::shared_ptr
)来自动管理内存。确保每个 malloc
对应一个 free
,每个 new
对应一个 delete
。使用工具如 Valgrind 进行内存泄漏检测。 3.解释C++中的RAII(Resource Acquisition Is Initialization)
RAII: 是一种编程习惯,即资源的获取和释放通过对象的构造函数和析构函数来管理。示例:#include <iostream>#include <memory>class Resource {public: Resource() { std::cout << "Resource acquired" << std::endl; } ~Resource() { std::cout << "Resource released" << std::endl; }};void useResource() { std::unique_ptr<Resource> res(new Resource()); // 使用资源}int main() { useResource(); return 0;}
4.解释栈区和堆区的区别
栈区: 用于存储函数调用的局部变量。内存由编译器自动分配和释放。具有后进先出的特点。内存分配效率高,但大小有限。堆区: 用于动态内存分配。内存由程序员手动分配和释放。大小不固定,可以动态增长或缩小。内存分配效率较低,但灵活性高。5.如何实现自己的内存池?
内存池是一种预分配大块内存以减少多次分配开销的方法。可以通过链表管理内存块,分配时从链表中取出一块内存,释放时将内存块重新挂回链表。
#include <iostream>#include <vector>class MemoryPool { std::vector<void*> pool;public: MemoryPool(size_t size, size_t count) { for (size_t i = 0; i < count; ++i) { pool.push_back(malloc(size)); } } void* allocate() { if (pool.empty()) return malloc(1); // 返回新分配内存 void* ptr = pool.back(); pool.pop_back(); return ptr; } void deallocate(void* ptr) { pool.push_back(ptr); } ~MemoryPool() { for (auto ptr : pool) { free(ptr); } }};int main() { MemoryPool pool(256, 10); void* p1 = pool.allocate(); pool.deallocate(p1); return 0;}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。