C语言内存分配方式

一.前言

C语言中存在几种内存分配方式,这里进行简单的归纳总结。(着重对动态内存进行讲解)

二.三种主要内存分配方式

  1. 从静态存储区域分配.
  2. 在栈上创建.
  3. 从堆上分配(动态内存分配).

三.三种主要内存分配方式的特点

  1. 从静态存储区域分配:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量、static变量.
    静态开辟内存:所开辟的内存是在栈中开辟的固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在栈上静态分配内存。不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,是代码中写死的。
  2. 在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限.
  3. 从堆上分(动态内存分配):程序在运行的时候用malloc或new申请任意多少的内存,但是程序员自己负责在何时用free来释放内存.动态内存的生存期由用户决定,使用非常灵活,但也会伴随着很多的问题.

四.几种常见的动态内存分配函数

  • malloc

  • calloc

  • recalloc

  • free

上面这几个函数的头文件:

#include <stdlib.h> //动态内存分配都可以使用
#include <malloc.h>
#include <calloc.h>
#include <recalloc.h>

下面进行详细的介绍:

1.malloc
平常写代码大部分都使用malloc

void *malloc(
   size_t size
);

1.在堆内分配内存块且以字节为单位 使用malloc需要告诉字节数大小 ,参数size为要分配的字节数.
2.返回值为空则为失败,不为空则意味着成功.
3.返回申请到连续内存块的首地址,需要将返回的首地址指针强制转换成目标类型的指针如下:

int* p = (int*)malloc(n * sizeof(int));//x*y x为申请空间个数 y为单个空间的大小
assert(p != NULL); //一般与定义搭配使用

注意:

  1. 一般在定义malloc后搭配一个断言 前定义后断言目的使为了保证有意义,如果为空没有意义分配失败.
  2. 使用malloc前(强转)后(sizeof)括号里类型不一致 ,sizeof里面不需要*.

2.calloc

void *calloc(
   size_t number,
   size_t size
);

1.参数为元素数量和每个元素的长度(以字节为单位).
2.在malloc的基础上将初始值全部变为0 ,使用较少;一般用for循环和malloc代替代码如下:

int* p = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; i++)
{
	p[i] = 0;
}

int* p1= (int*)calloc(10,sizeof(int));//这句话相当于上面的代码 

注意:
calloc中两个参数不要写反.

3.realloc

void *realloc(
   void *memblock,
   size_t size
);
  1. 重新分配内存空间 调整之前内存块的大小 扩大 或 缩小 均可以; 缩小内存块的话,开头地址不变并且其他不要的数据,也没有变化只是有边界线缩小.
  2. 参数memblock 指向之前已分配内存块的指针,size 为新开辟内存块的大小(字节).
int* p1 = (int*)realloc(p1, sizeof(int) * 2);//将p1扩容两倍

对于realloc分配空间:
成功or失败
1.分配成功:两种
1.1:原先内存后面如果有足够的空间,则直接续给你
1.2:原先内存后面的空间不足,没有办法给你续上,则只能重新再堆内存中找足够大的连续的内存,分配给你,这时只需将前面的空间的有效值拷贝过去然后将前面的空间释放掉.
2.分配失败
2.1:分配失败原因:堆内连续内存不够

对于内存的分配扩增倍数:
1.5倍 :4 6 9 可以将之前释放的内存整合一下,内存空间有可能再次利用到.
2.0倍: 4 8 16 永远没有办法将前面释放的内存整合起来(需要扩充的内存永远比自己之前释放的总和还要大).

注意:

  1. 第一个参数,必须是开辟前的内存块的开始地址,不能移动.
  2. 如果第一个参数传0(NULL),则相当于一个malloc.
  3. 第二个参数是重新开辟总的字节数而不是在原先内存基础上扩充的字节数.
  4. 如果将第二个参数传0,则相当于一个free函数.

上述的几个函数是对于内存空间的开辟,开辟后则必然会有相应的释=释放函数

4.free

void free(
   void *memblock
);
  1. 参数memblock 是指要释放的以前分配的内存块。
  2. 一个malloc搭配一个free.
  3. 使用free的必要性:如果动态内存开辟,而使用结束后,没有释放,会导致申请的这块内存没有人可以使用,一般将这块没有人可以使用的内存叫做垃圾,留有垃圾的函数或程序会导致内存泄露。
    ( 内存泄露或内存碎片的解决方法:重启设备
  4. 同一块内存连续释放.
  5. 假如free释放内存块a,但其参数并不是a的开始地址
  6. free结束后会造成一个悬空指针.
  7. 一般来说free函数执行结束后 一般会立刻将其变成空指针。

使用free:

free(void*ptr);

悬空指针和空指针的区别:

  1. NULL 空指针:指向0地址的指针 非0/NULL即真

  2. 悬空指针:(野指针) 这个指针可以指向任何一个空间 有修改的风险 会造成不可知的错误。

> 五.检测泄漏

对于free释放这块 我们会经常在不经意之间忘记释放函数 这也就造成了我们会有内存的泄露 这里给推荐一个内存泄漏的工具(适用于vs2019)
https://blog.csdn.net/qq_22108657/article/details/120884329
这个博主对这个泄露工具安装进行详细介绍讲解 大家可以去康康 安装后测试一下。

(小白一枚 这是自己的一点心得总结 写的可能不完善 欢迎各位大佬指正)

上一篇:Spring Boot 启动流程源码分析


下一篇:免费商用素材整理