目录
一、动态内存管理函数:
1、malloc:
2、free:
3、calloc:
4、realloc:
二、常见的错误:
1、对NULL指针解引用:
2、对动态开辟空间的越界访问:
3、对非动态开辟内存的释放:
4、使用free释放动态内存开辟的一部分:
5、对同一块内存多次释放:
6、动态内存开辟的空间忘记释放:(内存泄漏)
前言:动态内存管理是在内存中的堆区上进行维护的:
这个函数是向内存申请一块连续可用的空间,如果开辟成功,则返回指向这块空间的指针,如果开辟失败,就返回空指针,又因为返回类型是void* 所以要强制转化为自己想用的类型
例如:申请40个字节将他们赋为1-10:
int main()
{
int* p = (int*)malloc(40);
for (int i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
在申请内存之后,当这个程序结束后,所申请的内存就会还给操作系统,但是如果在某些大型程序,会一直跑,如果此时不主动将申请的内存还给操作系统的话就会导致内存泄漏的问题。
这个函数和malloc函数比较像,都是向内存申请函数,返回值也是一样的,void* 做返回值,如果要用什么就强制类型转化成什么,与malloc不同的是,传的形参不同,malloc是直接传需要开辟多少个字节,
calloc的传参中,size_t num是要分配的元素数。
size_t size是每个元素的大小。
calloc意思是申请10个元素空间,每个元素有4个字节。
上面两行基本上是等价的,不同的是,calloc申请空间后会将所申请得到的空间全都初始化为0,但是malloc不会。
realloc是调整申请到的内存空间大小,如果我们申请过大或者过小,那么就会导致浪费空间或者空间不足,所以就就要用到realloc进行动态开辟内存大小的调整。
在中,ptr是要调整的内存地址,size为调整后新的大小,返回值为调整后内存的起始位置。
realloc调整内存有两种情况:
1、待调整内存的后面空间足够放得下,直接开辟后返回起始位置。(如果传了空指针过来,那么就相当于malloc函数)
2、待调整内存的后面空间不够继续开辟,那么在堆上另一个内存位置开辟一个适合的空间,然后将原空间的元素拷贝过去,接着将这个新的内存地址返回,释放原空间。
当开辟空间比较小时,ptr和p的地址一样。
当开辟的空间比较大时候,ptr和p地址不一样就在另外的地方开辟了。
int main()
{
int* p = (int*)malloc(INT_MAX);
*p = 20;
free(p);
return 0;
}
如上,如果p没有开辟好内存,那么会返回一个空指针,若未进行判断,就会对NULL解引用
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("malloc fail");
return 1;
}
for (int i = 0; i <= 10; i++)
{
*(p + i) = i;//当i是10的时候越界访问
}
free(p);
return 0;
}
上面当i为10的时候就会越界访问,类似于p[10],但实际上只能访问到p[9].
int main()
{
int a = 1;
int* p = &a;
free(p);
return 0;
}
别“杀红了眼”,将不是动态开辟内存的空间给释放了,这个是在栈上面开辟的,而不是在堆上
int main()
{
int* p = (int*)malloc(100);
p++;
free(p);
return 0;
}
这个时候p已经不指向起始位置了,释放一部分肯定不行。
int main()
{
int* p = (int*)malloc(100);
free(p);
//...
free(p);
return 0;
}
有一个比较好的解决办法,就是每次释放完后将释放的那个指针置空,这个习惯很好。、
int main()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
perror("malloc fail");
return 1;
}
//...
return 0;
}
每次都申请空间,当这个程序结束后,所申请的内存就会还给操作系统,但是如果在某些大型程序或服务器,会一直跑,如果此时不主动将申请的内存还给操作系统的话就会导致这个内存被一点一点地“吃掉”,越来越少,最后程序就挂掉了。