文章目录
前言
本篇文章主要介绍malloc&calloc&realloc函数的使用、特点和可能出现的相关问题。
引入
为什么要引入这些动态空间申请函数里?
通俗的说就是:一方面可以说原有的固定空间已经满足不了你的需求,另一方面也可以说是为了空间的合理调配,避免造成空间的浪费。虽说计算机发展到了今天,空间的问题相比时间问的解决来说已是比较简单的了,扩容就可以解决一大部分问题,但是本着以最小的代价获得最大的收获,这样来说动态空间申请就派上了用场。
一、malloc函数
1.特点
malloc函数是一个动态空间申请函数
1>空间开辟的大小是固定的
2>数组在申明的时候,必须指定数组的长度,它所需的内存在编译时分配
3>在一些时候我们并不能准确知道我们所需的空间大小,这时候我们要使用动态空间开辟
4>mallo函数内部一个参数为所需分配的字节数大小
5>不会进行初始化
2.实现
void test1()
{
int size = 10;
int* p1 = (int*)malloc(sizeof(int) * size);//开辟10个int类型的空间
int* p2 = (int*)malloc(size);//默认分配是以1个字节为单位
//由于默认是(void*)类型,注意要进行强制类型转换
//int* p3 = (int*)malloc(sizeof(int) * LLONG_MAX);//测试申请失败
if (p1 != NULL)
{
printf("申请空间成功!\n");
for (int i = 0; i < size; i++)
{
printf("%d ", *(p1 + i));
}
}
else
{
perror("malloc:");
}
free(p1);
p1 = NULL;
}
注意:在指针没有利用价值的时候一定要及时销毁并置为空!!!
提示:perror会指出错误类型。
二、calloc函数
1.特点
1>calloc函数有两个参数 num个数 size大小
2>函数会进行自动初始化 全部初始化为0
3>相比较malloc会方便一点点
2.实现
void test2()
{
int size = 10;
int* p1 = (int*)calloc(size,sizeof(int));//开辟10个int类型的空间
int* p3 = (int*)calloc(LLONG_MAX,sizeof(int));//测试申请失败
if (p1 != NULL)
{
printf("申请空间成功!\n");
for (int i = 0; i < size; i++)
{
printf("%d ", *(p1 + i));
}
}
else
{
perror("malloc:");
}
free(p1);
p1 = NULL;
}
三、realloc函数
1.特点
1>relloc相比malloc和calloc更加灵活
2>有时我们会发现过去申请的空间小了,有时候我们会感觉申请的空间大了,这时relloc就派上用场了
3>要是我们预先申请的空间不够了,我们可以用relloc进行扩容,( 要是本身后面有足够的空间话就直接往后续) relloc函数会调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间, 并且自动把地址改为新的,自动释放原来的
4>函数类型为(void*)
5>注意最后的空间大小一共是新的申请的大小,而不是在原来的基础上又加了新的大小
6>通俗的说就是说这个地方容不下你的发展了,去给你找个新的地方发展,原来的地方释放掉
2.实现
void test3()
{
int* p = (int*)malloc(100);//malloc开辟100字节空间
//p的地址:0000027C13749A50
if (p != NULL)
{
printf("申请成功!\n");
}
else
{
perror("malloc:");
}
//扩展容量
p = (int*)realloc(p, 1000);
//p的地址:0000027C13752890
//下面为第二种方式扩容,自己新创建个指针变量,其实没必要这样我个人认为
//int* p1 = NULL;
//p1 = relloc(p, 1000);
//if (p != NULL)
//{
// printf("申请成功!\n");
//}
//else
//{
// perror("malloc:");
//}
free(p);
//free(p1);
p = NULL;
//p1 = NULL;
}
四、常见动态内存错误
1.对NULL指针的解引用操作
void test4()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;
//如果p的值是NULL,就会有问题。 加入assert()可以准确定位到错误位置
free(p);
p = NULL;
//一定要养成不用的时候给释放掉并且给NULL
}
2.对动态开辟空间的越界访问
void test5()
{
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("malloc:");
}
else
{
for (i = 0; i <= 10; i++)
{
*(p + i) = i;
//当i=10时为越界访问
//运行时会出错
printf("%d", *(p + i));
}
}
free(p);
p = NULL;
}
3.对非动态开辟内存使用free释放
void test6()
{
int a = 10;
int* p = &a;
free(p);//error 不是动态内存开辟的空间不能用free释放
p = NULL;
}
4.使用free释放一块动态开辟内存的一部分
void test7()
{
int* p = (int*)malloc(100 * (sizeof(int)));
p++;
free(p);//这样是错误的,不能进行部分空间释放,
//也不是从这个地方开始释放固定大小的空间
p = NULL;
}
5.对一片空间多次进行释放
void test8()
{
int* p = (int*)malloc(100 * (sizeof(int)));
free(p);
free(p);//error 不能多次释放
}
6.对动态开辟的内存忘记释放(内存泄漏)
void test99()
{
int* p = (int*)malloc(100 * (sizeof(int)));
if (p != NULL)
{
*p = 20;
}
}
void test9()
{
test99();
while (1);//这样内存会一直往上涨,但是我们一般关闭这个窗口后就停止了
//编译器会帮助我们会释放掉
}
注:编者水平有限,若有错误,敬请指正!!!