"痛苦难以避免,而磨难可以选择。"-->村上春树
作者:Mylvzi
文章主要内容:动态内存管理
所有的与动态内存分配有关的函数都包含于
这个头文件之中
作用:在堆区之中申请size字节大小的空间,并返回开辟内存空间的起始地址;
注意:
1.在申请内存的过程中有可能申请失败,如果申请失败,会返回NULL;(一定要进行判断,对空指针的解引用是非法的)
2.malloc的返回值是void*,使用者可以根据自身需求进行强制类型转换;
3.动态开辟的内存并不会自动销毁(还给操作系统),销毁有两种方式,使用free函数释放内存空间,程序退出;
作用:释放之前已经被动态内存函数(malloc,calloc,realoc)开辟的内存空间,将内存空间还给操作系统
注意:
1.只能释放动态开辟的内存,不能释放在栈区开辟的内存:
2.释放之后,要及时对释放空间的起始地址置于NULL,避免野指针的出现
代码示例:
int main()
{
int* p = (int*)malloc(40);//在堆区申请40byte空间
//判断malloc函数是否成功在堆区申请到空间,失败,返回NULL
//成功,返回所申请空间的初始地址
if (p == NULL)
{
perror("malloc");//打印错误信息
return 1;
}
//使用完毕,使用free函数对内存空间进行释放(和栈区内存的申请是不同的)
free(p);
p = NULL;//及时将释放空间的起始地址置于空指针
/*free函数只能释放动态开辟的内存空间*/
//int a = 10;
//int* ptr = &a;
//free(ptr);//err
return 0;
}
作用:向内存申请num个size字节大小的内存空间,并把内存空间的内容初始化为0 (malloc函数并不会进行初始化)
代码示例:
//calloc函数-->void* calloc(size_t num,size_t size)
int main()
{
//int arr[10];
int* p = (int*)calloc(10, 4);
if (p == NULL)
{
perror("calloc");
return 1;
}
//打印数据
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);//打印10个0
}
//使用完毕,释放内存空间
free(p);
p = NULL;
return 0;
}
作用:将ptr所指向的内存空间的大小改为size字节(增容)
但是,堆区的内存空间是有限的,在重新分配内存大小时,可能会出现内存空间不足的情况,导致返回值有三种情况;
1.返回旧空间的起始地址
2.返回新开辟空间的起始地址
3.返回NULL
当返回值不确定时,重新创建一个变量来接受
代码示例:
//realloc函数
//void* realloc(void* ptr ,size_t size)
int main()
{
//动态开辟一块内存空间
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
//赋值
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i + 1;
}
//增容
int* ptr = (int*)realloc(p, 80);//增容为80byte
if (ptr != NULL)//只要不是NULL,就证明开辟成功
{
p = ptr;
ptr = NULL;
}
else
{
perror("realloc");
return 1;
}
for (i = 0; i < 20; i++)
{
printf("%d ", p[i]);
}
//使用完毕
free(p);
p = NULL;
return 0;
}
int* p = (int*)malloc(40);
*p = 20;//未检测返回值是否为NULL
free(p);
p = NULL;
int* p = (int*)malloc(20);//申请了一个能容纳5个int类型数据的空间
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i + 1;//超过内存限制
}
int a = 10;
int* p = &a;
free(p);
p = NULL;
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
p++;
free(p);//ERR p此时已经不再是起始位置,不能释放内存的一部分
p = NULL;
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
free(p);
free(p);//err 已经释放过的不能再被释放
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
总结:动态内存的开辟虽然方便,但它是把双刃剑,会带来其他危险,在使用过程中要注意规范,养成以下习惯可以规避一些风险
1.在使用动态内存函数开辟完空间后,及时判断是否申请成功(p是否为NULL)
2.要及时释放动态开辟的内存(谨记malloc和free是成对出现的),并在释放后将地址置于NULL,避免野指针的出现;
3.在接受realloc函数的返回值时,最好创建一个新的变量来接收,并判断是否增容成功
补充一点:
free函数的参数中只有一个void* 类型的地址,并未告诉你对应空间的大小,那他是如何精准释放空间呢?原因在于,在malloc函数分配内存时,系统会在你所申请的内存之前(或之后,具体看编译环境)设置一个“头部信息”,free函数在得到起始地址后,会根据头部信息的内容知道内存的具体大小,从而实现对内存的精准释放
解决方案:你想改变实参str的值,为实参开辟空间,改变实参,要传递实参的地址!
void GetMemory(char** p)//使用二级指针存放str的地址
{
*p = (char*)malloc(100);//对p解引用,得到str所在的空间
} //为str分配足够的空间
void Test(void)
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);//输出hello world
}
注意:返回栈区空间地址,产生野指针,非法访问内存
free之后一定要及时将旧地址置为空指针
总结:动态内存分配是一种管理内存的重要方式,要了解与动态内存管理有关的函数(malloc,calloc,realloc,free),熟记动态内存分配过程中的危险(是否分配成功,realloc函数的返回值有三种情况),了解基本的内存分配知识;感谢大家观看