【什么?你说你还没学会动态内存】

  • Post author:
  • Post category:其他




一、为什么要有动态内存?(绝不是随随便)

内存分配有两种方式:静态分配和动态分配。

静态分配

1)在程序编译或运行过程中,按事先规定大小分配内存空间。

2)必须事先知道所需空间的大小。

3)分配在栈区或全局变量区,一般以数组的形式。

4)

按计划分配



动态分配

1)在程序运行过程中,根据需要大小自由分配所需空间。

2)

按需分配



举个例子

我们在定义数组时,长度往往是预先给定的,在整个程序中固定不变。

int a;
int b[10];//数组的长度固定了,只能是10个

所开辟的内存是在栈中开辟的固定大小的,如a是4字节,数组b是40字节,并且数组在申明时必须指定其长度 。如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在栈上静态分配内存。不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,不管你使用了多少。

但是在实际的编程中, 往往会发生这种情况,想在程序运行时才确定一个数组的大小,

即所需的内存空间取决于实际输入的数据 ,而无法预先确定

int b[10];
int b[20];//不能重新指定大小

那么如何解决上述问题? C语言提供了一些内存管理函数 ,这些内存管理函数可以

按需要动态的分配内存空间

,不需要时也可以把这一块空间回收,等待系统的下次分配。



二、动态分配内存函数(程序员必备基操)

在C中动态开辟空间需要用到三个函数 :

malloc(), calloc(), realloc() ,这三个函数都是向堆中申请的内存空间。

在堆中申请的内存空间不会像在栈中存储的局部变量一样,函数调用完会自动释放内存 , 需要我们

手动释放

,就需要free()函数来完成。



1、malloc 函数

头文件:#include<stdlib.h>

函数原型: void *malloc(unsigned int size);

功能说明:


在内存的动态存储区 ( 堆区 ) 中分配 一块长度为 size 字节的连续区域 ,用来存放类型说明符指定的类型



函数原型返回 void* 指针,使用时必须做相应的强制类型转换 ,分配的内存空间内容不确定,一般

使用memset 初始化



返回值: 分配空间的起始地址 ( 分配成功 )、NULL( 分配失败 )

注意:

1)在调用 malloc 之后,一定要判断一下是否为空,申请内存成功之后再使用。

2)如果多次malloc 申请的内存,第 1 次和第 2 次申请的内存不一定是连续的,不同编译器有不同的特点。

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....使用操作
}

申请了存放n个int类型数据的内存空间,并且用指针p指向其首地址。如果申请成功,即指针空不为空,那就可以使用。



2、 calloc 函数

头文件: #include<stdlib.h>

函数定义: void * calloc(size_t nmemb,size_t size);

size_t 是无符号整型,它是在头文件中,用 typedef 定义出来的。

函数的功能:在内存的堆中,申请 nmemb 个内存块,每块的大小为 size 个字节的连续区域

函数的返回值:

返回申请的内存的首地址(申请成功)

返回 NULL (申请失败)

注意:

malloc 和 calloc 函数都是用来申请内存的



区别:

  1. 函数的名字不一样
  2. 参数的个数不一样
  3. malloc 申请的内存,内存中存放的内容是随机的,不确定的,而 calloc 函数申请的内存中的内容为 0,

    会自动初始化



3、 free 函数

头文件: #include<stdlib.h>

函数定义 :void free(void *ptr);

函数说明:free函数释放ptr指向的内存。

注意:

ptr指向的内存必须是malloc、calloc、relloc动态申请的内存

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
free(p);
free(p);

是错的,已经释放过的内存不能重复释放,会出现内存错误。

正确用法是:

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....需要进行的操作
}
//操作完成 ,不再使用这片内存空间
free(p);
p = NULL;

为了确保后面过程误用了指针p,所以在free释放了p之后,还需要让其指向NULL。



4、realloc 函数

动态内存的精华所在这里,当我们使用 malloc 或 calloc 申请了一块内存,使用一段时间之后发现不够用或者内存分配太多了,就可以在原先基础上重新指定分配一块内存下来。

头文件:#include<stdlib.h>

函数的定义: void* realloc(void *s,unsigned int newsize);

函数的功能:

在原先 s 指向的内存基础上重新申请内存,新的内存的大小为 new_size 个字节



如果原先内存后面有足够大的空间,就追加,如果后边的内存不够用,则 relloc 函数会在堆区 找一个 newsize 个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。

如果 newsize 比原先的内存小,则会释放原先内存的后面的存储空间,只留前面的 newsize 个字节

返回值:新申请的内存的首地址。



三、内存泄漏(程序员大忌)



什么是内存泄漏?

顾名思义,就是系统中的内存泄漏了,我们不能正确地使用这一块地内存,也就是说:

出现了内存的浪费,不能正确使用这一块内存



为啥会用不上?因为内存是靠指针来使用的,当这块指针出现了错误或者找不到了的时候,我们就不能再使用其指向的内存。

当我们使用动态内存空间之后,却忘记了释放,那么这一块内存就不能被系统回收,一直占用着,导致了内存的浪费。

所以,作为一名程序员,一定要把free记心中。



四、总结



常见的动态内存使用错误

1)对NULL指针的解引用操作

在使用malloc、calloc、realloc申请内存之后,要对其返回值判断是否为空。

2)对动态开辟内存空间的越界访问

申请动态内存之后,其也是有大小的,越界访问会出现问题。

3)使用free函数释放一块动态内存的一部分

free函数释放内存时,要从内存的起始位置开始释放。

4)对非动态开辟内存使用free函数释放

free函数只能针对动态申请的内存空间。

5)对同一块动态内存多次释放

6)动态内存空间忘记释放(内存泄漏)



版权声明:本文为awenduxing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。