c语言中的动态内存分配

动态内存分配

传统数组的缺点:

  1. 数组长度必须事先定好,且只能是常整数,不能是变量

    int len = 5;  
    int a[len];//error,会报错
    
  2. 传统形式定义的数组,该数组的内存无法手动释放

    在一个函数(主函数或者自定义函数)运行期间,系统为该函数中定义的数组分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放

  3. 数组的长度一旦定义,其长度就不能在改变。即数组的长度不能在函数运行的过程中动态的扩充或缩小

  4. 传统方式定义的数组不能跨函数使用,在A函数中的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法在其他函数使用。

使用动态内存分配可以很好地解决上面的问题

动态分配内存需要用到malloc()函数,使用例子如下:

int i = 5;//静态分配
int * p = (int *)malloc(4);
*p = 5;
  1. malloc(memory allocate)函数在malloc.h头文件里定义

  2. malloc函数原型是void * malloc(size_t size),可以看到malloc函数返回的是一个void *类型的指针(地址)

  3. malloc函数只有一个形参,并且形参是整形

  4. 4表示请求系统为本程序分配4个字节

  5. malloc函数只能返回第一个字节的地址

  6. 由于malloc只返回第一个字节的地址,因此不知道【变量p指向的数据】到底占多少个字节,因此需要在malloc(4)前加上一个强制转换(int *),这样就知道【变量p指向的数据】占的是4个字节。

    补充:malloc()前是否需要加强制转换

    在网上搜了一通后,个人理解是:如果后续代码需要移植到c++去,就要加,如果只是用c,那可加可不加。

    参考了这些内容

    1. Why do we cast return value of malloc?
    2. Do I cast the result of malloc?
    3. 为什么C语言里不推荐强制转换malloc的返回值?
    4. What’s wrong with casting malloc’s return value?
    5. malloc的强制转换
    6. 为何malloc 前面要强制类型转换?

    上面这些内容,大致意思都是:

    • 可以不使用强制转换来直接使用malloc,因为在c语言中支持隐式转换

    • 上面的内容会有提及说最好不加,都是因为使用强制转换还可能掩盖malloc()声明错误时产生的重要警告

    • 如果提及说要加,则一是因为malloc默认返回的是void *类型的,二是因为有些老旧的编译器不支持隐式转换,三是考虑到代码的移植性才需要加(c可以不加,但是后续转c++运行的话不加会出错)

  7. 语句int * p = (int *)malloc(4);共分配了8个字节,p变量占4个字节,【p所指向的内存】也占4个字节

  8. p本身所占的内存】是静态分配的,【p所指的内存】是动态分配的

  9. 语句*p = 5;中的*p代表的就是一个int变量,只不过*p这个整型变量的内存分配方式和i变量的分配方式不同

动态内存分配的小例子

利用malloc()函数确定一维数组的长度:

#include <stdio.h>
int main(void)
{
    int a[5];//静态分配
    
    int len;
    int * pArr;
    puts("请输入数组长度");
    scanf("%d",&len);
    pArr = (int *)malloc(len * sizeof(int));
    
    return 0;
}

本例中构造的动态一维数组其实类似于这个语句:int pArr[len],数组每个元素的类型是int,数组名称是:pArr

补充

当系统内存空间很小的时候,可能会没有足够空间来使用malloc(),因此,此时最好加多一个判断语句:

int *p = (int *)malloc(200);
if(p == NULL)
{
    printf("malloc error");
    exit(-1);
}

free()

malloc()通常都需要与free()配套使用:

int * p = (int *)malloc(4);
...;
free(p);

free(p)表示把【p所指向的内存】给释放掉

p本身的内存是静态的,不能手动释放,p本身的内存只能在p变量所在的函数运行中止时由系统自动释放

当p指针不再指向上面分配的空间的地址而又没释放掉,那这部分分配的空间就会一直存在,一直到程序执行完毕才会被释放掉

释放掉p所指的内存空间后,为了防止指针p成为野指针(也可以说成防止悬挂指针),通常会再加多一句:

p = NULL;

realloc()

如果用了malloc函数后发现开辟出来的内存空间不够用,可以用realloc函数重新划分空间。realloc()函数在<stdlib.h>中定义,这个函数有两个参数,一个是指向先前开辟的内存块的指针,一个是调整内存空间的字节数。realloc()函数返回的也是void *类型的指针。使用realloc()函数例子如下:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int *p;
    p = (int *)malloc(20);
    ...;//一通操作后发现开辟的20字节内存不够用
    int *p2;
    p2 = (int *)realloc(p,40);//重新调整内存空间
    ...;
    return 0;
}

上面代码中,其实指针p和指针p2所指向的地址都是一样的,它们都指向这个空间的起始地址,只是这个内存空间通过realloc()调整后变大了。因此也可以直接使用这个函数,而不用再定义一个p2

realloc(p,40);

memset()

可用于初始化新开辟空间里面的内容

函数的原型是void * memset(void *str, int c, size_t n),使用该函数需要加上#include <string.h>

第一个参数是新开辟的内存空间

第二个参数是要初始化的内容

第三个参数是大小

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    int *p = (int *)malloc(4*sizeof(int));
	int i;
	for(i=0;i<4;i++)
		printf("%d  ",*(p+i));
	printf("\n初始化后:\n");
	memset(p,0,12);
	for(i=0;i<4;i++)
		printf("%d  ",*(p+i));
    return 0;
}

输出结果:

7493488  0  7471440  0
初始化后:
0  0  0  0

静态内存和动态内存的比较

  1. 静态内存是由系统自动分配,由系统自动释放

  2. 静态内存是在“栈”分配的

  3. 静态内存在函数执行期间可以被其它函数使用,当函数执行完毕之后就不能再被其它函数使用了

    void func1()
    {
        int i = 0;
        func2(i);
        ...
    }
    void func2()
    {
        ...;
    }
    

    func1函数开辟的静态内存中存储着变量i。当函数func1执行时,func2可以访问到变量i,但是当func1执行完后,变量i的内存空间会被释放掉,这时其它函数都访问不了变量i

  4. 动态内存是由程序员手动分配,手动释放

  5. 动态内存是在“堆”分配的

  6. 在某个函数中划分的动态内存,在这个函数执行完毕之后依然不会消失,可以被其它函数使用

    void func1(int **q)
    {
    	*q = (int *)malloc(sizeof(int));
    	**q = 5;
    }
    int main(void)
    {
    	int *p;
        func1(&p);
    	printf("%d\n",*p);
        return 0;
    }
    

    func1运行完毕后,开辟的内存空间并不会被释放掉,因此依然能访问到这部分的内存空间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值