C语言中malloc与free

一、为什么c语言中要有malloc

malloc就是memory allocate动态分配内存,malloc的出现时为了弥补静态内存分配的缺点,静态分配内存有如下缺点:

1、比如说,传统的一维数组,如int a[5],使用传统的一维数组需要事先指定数组的长度,而且数组的长度必须是一个常量(宏定义的 常量)

2、传统数组(静态分配),不能手动释放,只能等待系统释放,静态分配的变量在该函数内运行的时候有效,当静态分配的变量所在函数运行完之后,该内存会自动释放。静态分配的内存,是在栈中分配的,其实在C语言中的函数调用也是通过栈来实现的,栈这种数据结构的一个特点就是(先进后出),所以,在调用函数的时候,都是先压入栈中,然后,再从最上面的函数开始执行,最后,执行到main函数结束。动态分配通过malloc分配,是在堆中分配的,堆不是一种数据结构,它是一种排序方式,堆排序。

3、传统数组的长度一旦定义之后,就不能更改,比如说,如果我有一个业务在这之前给分配的大小为100,但是,我现在由于业务数量的增长,原来的大小就无法满足。

4、静态分配不能跨函数调用,就是无法在另一个函数中,来管理一个函数中的内存。静态分配,只在当前函数有效,当,静态分配所在的函数运行完之后,该变量就不能被其他的函数所调用。

二、malloc是什么

malloc其实就是一个可以动态分配内存的函数,从而可以很好的弥补上面静态分配的缺点。

三、malloc怎么使用

1、使用malloc函数的时候,需要包含一个头文件#include <malloc.h>

2、malloc函数只接受一个形参如,int p = (int )malloc(sizeof(int)).先来解释下这句话的含义,int p代表一个以int类型地址为内容的指针变量,p这个变量占4个字节(某些计算机),这个p变量是静态分配的一个变量。在某些计算机的前提下,指针变量所占的大小都是一样的,无论是char 还是long ,因为,这些指针变量里面存放的是一个8位16进制的地址,所以占四个字节,当然这些都是在某些计算机的前提下,并不是所有的都是这样的。说道地址的话,就和计算机的地址总线有关,如果计算机的地址总线是32根,每根地址总线只有两种状态(1或0),32根地址线的话,如果全为1的话,刚好就是一个8位十六进制,一位十六进制等于四个二进制(2^4=16)。32根地址总线可以 表示210*210210*22种状态,可以表示的最大内存为4G,也就是说32根地址总线(也就是四个字节 的指针变量)最大可以表示4G内存。malloc函数会返回开辟空间的首地址,加(int *)的目的是让计算机知道,如何去划分这个开辟的空间,因为char、int 、long这些类型的字节大小是不一样的,我们知道了首地址,还要知道是以几个字节为单元。所以,这句话一共开辟了8个字节(某些计算机上),这也是为什么我写sizeof(int),而不是直接写4的原因。

3、malloc开辟空间所返回的首地址是动态分配的。

四、malloc函数的使用例子
# include <stdio.h>
# include <malloc.h>  
int main(void){
	int i = 5; //分配了4个字节 静态分配
	int * p = (int *)malloc(sizeof(4));//指针变量p为静态分配,malloc开辟的空间为动态分配
	*p = 5; //*p 代表的就是一个int变量,指针变量p表示是一个以int类型的地址为内容的变量
	free(p); //freep(p)表示把p所指向的内存给释放掉,p本身的内存是静态的,不能由程序员手动释放
		//p本身的内存只能在p变量所在的函数运行终止时由系统自动释放 
	return 0;
}
# include <stdio.h>
# include <malloc.h>
 
void f(int * q){
	*q = 200;
	//free(q);  //把q所指向的内存释放掉,不然后面在使用*p的时候会报错,因为p所指向的内容已经被释放了
}
 
int main(void){
	int * p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
	*p = 10;
	f(p);  //p是int *类型
	printf("%d\n", *p);
	return 0;
}

在 C 语言中,程序中 malloc 等内存分配函数的使用次数一定要和 free 相等,并一一配对使用。绝对要避免“malloc 两次 free 一次”或者“malloc 一次 free 两次”等情况。这就像我们的婚姻制度,必须是“一夫一妻制”,不能够“多夫一妻”或者“一夫多妻”,这些都是不合法的,如下面的示例代码所示:

#define MAX_BUF_SIZE 100
int main(void){
    /*内存释放标志*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL){
        /*...*/
    }
    if (flag == 0){
        free(p);
    }
    free(p);
    return 0;
}

在上面示例代码中,当条件“if(flag==0)”成立时,“free§”将被执行两次,从而导致内存的双重释放错误。因此,应该消除这种双重释放潜在的风险,保证动态内存只被释放一次,如下面的示例代码所示:

#define MAX_BUF_SIZE 100
int main(void){
    /*内存释放标志*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL){
        /*...*/
    }
    if (flag == 0){
        /*...*/
    }
    free(p);
    p=NULL;
    return 0;
}

当然,也可以采用下面的方式:

#define MAX_BUF_SIZE 100
int main(void)
{
    /*内存释放标志*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL){
        /*...*/
    }
    if (flag == 0){
        free(p);
        p = NULL;
    }
    if (p != NULL){
        free(p);
        p = NULL;
    }
    return 0;
}

除此之外,对于内存释放还必须保证只释放动态分配的内存,即不能用 free 来释放非 malloc、realloc、calloc 与 aligned_alloc 等内存分配函数分配的内存空间。与此同时,也不要将指针变量进行自增或者自减操作,使其指向动态分配的内存空间中间的某个位置,然后直接释放,这样也有可能引起未知的错误。

在 free 之后必须为指针赋一个新值

在使用指针进行动态内存分配操作时,在指针 p 被 free 释放之后,指针变量本身并没有被删除。如果这时候没有将指针 p 置为 NULL,会让人误以为 p 是个合法的指针而在以后的程序中错误使用它。

如下面的示例代码所示:

#define MAX_BUF_SIZE 100
int main(void)
{
    char * p = NULL;
    p=(char *)malloc(MAX_BUF_SIZE);
    if (p == NULL){
        /*...*/
    }
    /*内存初始化*/
    memset(p, '\0', MAX_BUF_SIZE);
    strcpy(p, "hello");
    /*释放内存*/
    if (p != NULL){
        free(p);
    }
    if (p != NULL){
        /*发生错误*/
        strcpy(p, "world");
    }
    return 0;
}

在上面的示例代码中,第一个判断语句:

/*释放内存*/
if (p != NULL){
    free(p);
}

虽然释放了指针变量 p,但这个时候指针变量 p 本身并没有被删除,其保存的地址并没有改变。但是,此时 p 虽不是 NULL 指针,但它却不指向合法的内存块,成为“野指针”或称为“悬垂指针”。接下来,在执行第二个判断语句时:

if (p != NULL){
    /*发生错误*/
    strcpy(p, "world");
}

条件“if (p != NULL)”成立,“strcpy(p, “world”);}”语句将被继续执行,导致程序出错。或许有人会问,“free§”到底释放了什么?

“free§”释放的是指针变量 p 所指向的内存,而不是指针变量 p 本身。指针变量 p 并没有被释放,仍然指向原来的存储空间。

其实,指针只是一个变量,只有程序结束时才被销毁。释放内存空间后,原来指向这块空间的指针还是存在的,只不过现在指针指向的这块内存是不合法的。因此,在释放内存后,必须把指针指向 NULL,以防止指针在后面不小心又被解引用了。

如下面的示例代码所示:

#define MAX_BUF_SIZE 100
int main(void)
{
    char * p = NULL;
    /*内存申请*/
    p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL){
        /*...*/
    }
    /*内存初始化*/
    memset(p, '\0', MAX_BUF_SIZE);
    strcpy(p, "hello");
    /*释放内存*/
    if (p != NULL){
        free(p);
        /*在free之后给指针存储一个新值*/
        p = NULL;
    }
    if (p != NULL){
        /*发生错误*/
        strcpy(p, "world");
    }
    return 0;
}

现在,通过语句“p=NULL”给指针变量 p 赋予一个 NULL 值之后,第二个条件语句“if(p!=NULL)”将不成立,语句“strcpy(p,“world”)”也将不会被执行。

综上:free§ 完之后,一定要将指针变量 p 置为 NULL。

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小飞睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值