静态内存分配是在编译时确定的,程序执行过程中无法改变所分配的内存大小;动态内存分配可以根本程序的运行环境来动态分配和释放空间,提供了更大的灵活性
有些数据结构的大小和结构在编译时无法确定,需要在运行通过动态内存分配来创建和操作
通过动态内存分配,程序可以根据实际需求分配合适的内存空间,避免了静态内存分配中可能出现的浪费内存的问题。动态内存分配可以提高内存的利用率,允许多个对象共享内存空间。
可以根据程序的运行状态来决定内存的分配和释放,使得程序具有更大的灵活性。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内容容量有限,栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等
是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信
一般由程序员分配释放,若程序员不释放,程结束时可能由OS回收,分配方式类似于链表
存放全局变量、静态变量。程序结束后由系统释放
存放函数体(类成员函数和全局函数)的二进制
函数原型如下:
void* malloc (size_t size);
malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
void*
,所有malloc函数并不知道开辟空间的类型,具体在使用者自己来决定size
为0,malloc的行为是标准还是未定义的,取决于编译器void free (void* ptr);
free函数用来释放动态开辟的内存
ptr
指向的空间不是动态开辟的,那free函数的行为是未定义的ptr
是NULL指针,则函数什么事情都不做#include
#include
int main()
{
int num = 10;
int* ptr = NULL;
ptr = (int*)malloc(num * sizeof(int));//动态开辟10个int类型的内存空间
if (NULL != ptr) //判断ptr是否为空,
{
int i = 0;
for (i = 0; i < num; i++)
{
*(ptr + i) = 0;
printf("%d ", *ptr);
}
}
free(ptr);
ptr = NULL;
return 0;
}
函数原型如下:
void* calloc (size_t num, size_t size);
num
个大小为size
的元素开辟一块空间,并且把空间的每个字节初始化为0malloc
的区别只在于calloc
会有返回地址之前把之前申请的空间的每个字节初始化为全0#include
#include
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (NULL != p)
{
for (int i = 0; i <10; i++)
{
*(p + i) = i+1;
printf("%d ", *(p+i));
}
}
free(p);
p = NULL;
return 0;
}
realloc
函数的出现让动态内存管理更加灵活,可以做到对动态开辟内存大小的调整。
函数原型如下:
void* realloc (void* ptr, size_t size);
ptr
是要调整的内存地址size
调整之后新大小
realloc
在调整内存空间存在的两种情况:1.原空间之后有足够大的空间——要扩展空间就直接在原有内存之后直接追加空间,原来空间的数据不发生变化
2.原空间之后没有足够大的空间——原空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用
#include
#include
int main()
{
int* p = (int*)malloc(5 * sizeof(int)); // 分配5个整型大小的内存块
if (p == NULL) {
printf("Memory allocation failed.\n");
return -1;
}
for (int i = 0; i < 5; i++) {
p[i] = i + 1; // 给每个元素赋值
printf("%d ", p[i]);
}
printf("\n");
int* new_p = (int*)realloc(p, 10 * sizeof(int)); // 将内存块大小重新分配为10个整型大小
if (new_p == NULL) {
printf("Memory reallocation failed.\n");
free(p); // 重新分配失败,释放原先分配的内存块
return -1;
}
for (int i = 0; i < 10; i++) {
new_p[i] = i + 1; // 给每个元素赋值
printf("%d ", new_p[i]);
}
printf("\n");
free(new_p); // 释放重新分配的内存块
new_p = NULL;
return 0;
}
申请和释放单个元素的空间,使用new
和delete
操作符,申请和释放连续的空间,使用new[]
和delete[]
(注意:匹配起来使用)
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[10];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
new
和delete
是用户进行动态内存申请和释放的操作符,operator new
和operator delete
是系统提供的全局函数,new
在底层调用operator new
全局函数来申请空间,delete
在底层通过operator delete
全局函数来释放空间
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道,operator new
实际也是通过malloc
来申请空间,如果malloc
申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete
最终是通过free
来释放空间的
如果申请的是内置类型的空间,new
和malloc
,delete
和free
基本类似,不同的地方是:new/delete
申请和释放的是单个元素的空间,new[]
和delete[]
申请的连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL
都是从堆上申请空间,并且需要用户手动释放
//c
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
//c++
void test()
{
int *p = new int(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
delete(p)
}
//c
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
//c++
void test()
{
int i = 0;
int* p = new int[10];
if (nullptr == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)
{
*(p + i) = i; // 当i是10的时候越界访问
}
delete[] p;
}
//c
void test()
{
int a = 10;
int *p = &a;
free(p);
}
//c++
void test()
{
int a = 10;
int *p = &a;
delete p;
}
//c
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
//c++
void test()
{
int* p = new int[100];
p++;
delete[] p; //p不再指向动态内存的起始位置
}
//c
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
//c++
void test()
{
int *p = (int *)malloc(100);
delete[] p;
delete[] p;//重复释放
}
//c
int* p1 = (int*)malloc(sizeof(int));
//c++
int* p2 = new int;
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死 。
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内
存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对
该段内存的控制,因而造成了内存的浪费 。
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死 。