边学边记——动态内存管理(包含malloc,calloc,realloc函数,柔性数组的介绍)

目录

一.为什么要动态内存管理

二.动态内存开辟

三.动态内存函数的操作

1.malloc函数(与free成对出现)

2.free函数

 3.calloc函数

4.realloc函数

 5.总结

(1). 野指针

 (2).动态内存管理中需要注意的问题

四.柔性数组

1.定义

2.特点

3.优点 

 4.注意


一.为什么要动态内存管理

首先,我们得了解以下几点:

1.变量名称其实是一种外在体现,是给程序员看的,在程序内部指令中都是通过地址进行操作的。

2.每个函数在调用的时候都会分配一块栈空间,作为函数的局部存储,函数内定义的局部变量,使用空间都是在函数栈空间中分配的。

3.一个函数内其实定义的局部变量并不是无限制定义的,如果定义一个超大变量,超过了函数栈空间大小就会报错。

在一个函数中定义一个变量的注意事项

局部变量:栈空间上内存分配。

1.不能过大,会超出函数栈空间。

2.变量出了作用域空间就会被释放。

3.当保存一组数据时(并不知道数据有多少),只能按照上限个数提前定义好数组,这样就会造成空间的浪费(实际用不了那么多空间)。

eg:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

由此可见:

1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。 这时候就尝试能否进行动态内存开辟(堆上的内存分配)。 

二.动态内存开辟

动态内存开辟:在堆区分配内存。

特点

1.手动申请,也必须手动释放(如果不释放,那么出了作用域也不会自动释放)。

对比:栈上空间的分配:定义了局部变量就会分配空间,出了作用域就会自动释放空间。

注意:申请空间后一定要释放,不释放的话就会造成内存泄漏。

2.需要多少申请多少(实现按需分配)。

3.空间在堆上分配,不占用栈空间(不怕函数栈溢出,一般情况下堆都不会溢出)。

PS:动态地址分配,申请的空间是一整块连续的空间,因此这块空间可以当作数组来用。  

三.动态内存函数的操作

1.malloc函数(与free成对出现)

void* malloc (size_t size);

头文件:#include<stdlib.h>

功能:向内存申请一块连续可用的空间,并返回指向这块空间的指针。

返回值

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

注意:如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2.free函数

void free (void* ptr);

头文件:#include<stdlib.h>

功能:free函数用来释放动态开辟的内存,传入的是空间的首地址。

注意

1. 不要对局部变量进行free。

2.空间释放只能从动态申请的首地址释放,不能从中途释放。

3.释放的是ptr指向的空间,而不是p指针变量本身的空间。

 3.calloc函数

void* calloc (size_t num, size_t size);

头文件:#include<stdlib.h>

功能:申请一块空间,并将空间中的数据初始化为0。

参数:num是块个数;size是块大小;num*size就是要申请的空间大小。

返回值:成功返回空间首地址;失败返回NULL。

与函数 malloc 的区别:只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。  

4.realloc函数

void* realloc (void* ptr, size_t size);

头文件:#include<stdlib.h>

功能:对动态存储的空间在其基础上进行扩容。

参数:ptr是原先的空间首地址;size是要扩容到的空间大小。

返回值:成功有两种情况,第一种是原有空间足够大,那么就在原地址扩容成功,不释放该块空间;第二种是原有空间不够大,则会释放原先的空间,返回新的空间首地址;失败返回NULL。

如果realloc传入的源空间首地址为NULL,那么功能等价于malloc。

 5.总结

(1). 野指针

指向了一块不能正常访问的空间。

eg:如果申请了一块内存,将地址赋给了指针变量p,然后对p进行了free,这时候只是把p指向的空间给释放了,并没有修改p变量的指向,这时的p指针就是一个野指针。

(2).动态内存管理中需要注意的问题

  • 不要对局部变量的地址进行释放。
  • 动态申请的空间不要从中间释放。
  • 释放了指针指向的空间后,指针会变为野指针,要注意置为NULL,不要继续访问原空间。
  • 动态申请的空间不要重复释放。
  • 使用realloc函数扩容成功后,有可能原空间会被释放掉,因此一定要保存新的地址。
  • 申请与释放操作一定要成对出现。

四.柔性数组

1.定义

在一个结构体中,最后一个元素是大小未知的数组,数组的空间通过动态内存申请。

可以这样理解:柔性数组不是一个数组,是和结构体搭配使用的一种数据结构。

typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;

也可以将成员作为指针,独立进行空间的申请与释放,用的时候会麻烦一些,但会更灵活。

typedef struct st_type
{
int i;
int *p;//柔性数组成员
}type_a;

2.特点

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

3.优点 

  • 方便内存释放。
  • 有利于访问速度。

 4.注意

  • 柔性数组本身不占空间。
  • 柔性数组前边必须有一个成员,柔性数组要放在结构体的最后面。

如有建议或想法,欢迎一起交流讨论~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值