欢迎来到博主的专栏——C语言进阶指南
博主id已更新,希望大家多多支持新人博主
malloc,calloc,以及realloc用于开辟动态内存时,由于无法再堆区中找到合适的空间区域。此时这些函数会停止开辟内存空间,并返回一个null指针。
我们先来看看一下几段代码
void test1(void)
{
int* p = malloc(40);
*p = 10000;
free(p);
}
void test2(void)
{
int* p = calloc(1,40);
*p = 10000;
free(p);
}
void test3(void)
{
int* p = realloc(NULL, 40);
*p = 10000;
free(p);
}
test1,test2,和test3函数都对开辟的动态内存空间进行了操作,但是前面已经提到了,如果函数开辟动态内存失败了,返回的是一个NULL。也就是说此时p是一个空指针,对空指针进行解引用操作是一个错误的行为
解决方法如下:
对开辟的内存空间进行操作时,先判断这个指针的合法性
bool test1(void)
{
int* p = malloc(40);
if(p==NULL)//通过判断p是否为空指针来判断内存开辟是否成功
return false;
*p = 10000;
free(p);
}
现在大家的计算机对于开辟几万,几十万,几百万的个字节的申请简直不值一提。如果大家想见到内存开辟失败,可以使用如下方式
int *p=malloc(INT_MAX);
INT_MAX是一个C语言中的宏,是int类型的数据能得到的最大数值。
指针丢失的问题有多种情况
int* p = malloc(40);
if (p == NULL)
exit(EXIT_FAILURE);
*p = 10000;
free(p);
p是一个指向动态内存区域的指针,但是当这段区域被释放之后,p仍然指向这片区域,但是此时p已经不再有使用这个区域的权限。也就是说p已经变成了指向不合法内存空间的野指针
由上图可以发现,由p指向的这片区域已经被释放,这片内存区域已经不再提供指针使用了,而p仍然指向这片区域,所以这个p已然成为一个野指针
解决方案:
请确保自己或者别人不再使用这个指针。
如果不行,可以在指向的动态内存空间被释放之后,将这个指针置为空指针(NULL)。
int* p = malloc(40);
if (p == NULL)
exit(EXIT_FAILURE);
*p = 10000;
free(p);
p = NULL;
这个问题主要在使用realloc函数修改内存空间是遇见。由于realloc可以用来修改动态内存空间的位置,使用realloc函数时会遇到以下情况(包括但不唯一)
(a)调整空间的请求是合理的,申请通过,realloc返回调整空间后的起始地址。
(b)动态内存区域开辟失败,返回NULL。
为了省事,有人会用同一个指针来接收这个函数的返回值,因为如果这个函数修改成功了,这个指针是可以继续指向一个有效的动态内存空间的
int* p = malloc(40);
p = realloc(p, 80);
但是这种风格会导致一个问题,就是当realloc函数开辟动态内存失败时,返回的是一个NULL值,这就会导致这种情况
realloc函数开辟新区域后,会将原区域的数据拷贝进新区域,并将原区域的内存释放。
而realloc函数开辟新区域失败时,原区域将会被保留
于是原区域的内存,此时没有任何一个指针指向这片区域了。这就导致我们永远的丢失了这片区域,这片区域不会再开辟(因为已经具有权限),又无法将其释放(没有指针指向),
且不能使用(没有可以操作这片区域的指针)。
解决方法如下:
使用一个新指针来指向这个地方。
int* p1 = malloc(40);
int* p2 = realloc(p1, 80);
如果觉得使用多个指针会影响程序的可读性的话,可以用一个临时指针来替代。
int* p1 = malloc(40);
int* p2 = realloc(p1, 80);
if (p2 != NULL)
{
p1 = p2;
}
这样子在后续的程序当中,可以继续使用p1来指向重新调整的区域.
在C语言中,除了在静态区的变量以外的变量,都有其作用域,通常这个作用域在花括号以内**“{}”**
有时候因为项目的需要,一个动态开辟的区域不会在创建过后立即销毁,而是在后续执行某个操作之后,再将这个这个动态内存释放掉。
void getmemory(void)
{
int* p = malloc(40);
if (p == NULL)
exit(EXIT_FAILURE);
*p = 10000;
}
这个创建动态内存的函数具有一个问题,那就是当这个函数执行完成之后,指针由于超出作用域,被计算机系统释放掉。那么这片内存区域就不再具有指针指向了,后续的操作也无法使用到这片区域。
int* p = malloc(40);
if (p == NULL)
exit(EXIT_FAILURE);
*p = 10000;
return p;
虽然这个指针超出了作用域被释放了,但是函数结束后将这个地址传回,创建一个新的指针来接收这个地址,这个地址可以留着供自己或他人使用,并且可以被释放。
realloc根据内存的分配状态有着不同的调整内存的方式,以一个代码为例
int* p = malloc(40);
int* ptr = realloc(p, 80);
假设malloc开辟40字节大内存大小之后,返回给指针p的值为0x11223344,也就是这片动态内存的区域的起始地址为0x11223344.
在大部分程序中,不会仅仅只有一个部分的内存区域被开辟的。
也就是说这个代码在内存中可能会产生的内存区域为
(a)假如重新开辟的区域不会与其他的内存区域重合。
也就是说p指向的动态内存空间,在经过realloc函数重新调整之后,不会与粉色的内存空间发生重合。
那么此时realloc返回的地址值与p是一致的,也就是0x11223344.
(b)如果重新调整后的区域的后续区域重合
这种情况下,realloc函数会在内存的其他区域当中,找到一个存储空间合适的位置,将原区域(0x11223344)的数据拷贝至新区域,并将原区域的内存释放出去。realloc函数返回新区域的起始地址。
需要注意的是,虽然原区域被释放了,而且realloc函数的返回值也不再是原区域的起始地址,但是这不会修改p,也就是p仍然指向原区域(0x11223344),即使它已经被释放了。
(c)内存中找不到任一区域来存放新的地址
如果在内存当中,函数找不到一处合适的空间来符合调整后的字节需求,那么这时候realloc函数不会在内存中开辟新的空间,函数返回值为空指针NULL,原空间不会被修改。