前言
- 本博文基于VC++6.0开发调试
- 只是对这四个函数的一个总结;
- 动态内存分配这个知识经常和动态数据链表(如链表)结合使用;
什么是内存的动态分配
C语言中的全局变量和局部变量分别分配内存的栈(stack) 中的静态存储区和动态存储区,关于静态存储区和动态存储区之前的博客也有介绍(https://blog.csdn.net/wuyuzun/article/details/82354885)
在括号里的这篇博文里介绍过静态存储区和动态存储区内的变量是有生命周期的;全局变量被编译器编译时,开始分配其内存单元,生命周期持续到程序运行结束;局部变量比如函数参数,当程序被调用时为参数分配内存单元,函数调用结束生命周期也结束,所占用的内存单元被释放;那么除此之外还有一种情况,就是生命周期由用户自己决定,需要多少内存就开辟多少内存,用完了的时候释放就可以了,并且这些内存一般是用来存储一些临时数据,所以称为内存的动态分配,而这个存储区成为堆(heap);
所以将C语言中常用的存储区分类:
区名 | 生命周期 | 存储数据类型 |
---|---|---|
静态存储区 | 从定义处到程序结束 | 全局变量,静态变量(static) |
文字常量区 | 从定义处程序结束释放 | 常量字符串 |
程序代码区 | 从开始执行程序的开始 ,到程序结束 | 二进制代码 |
栈(stack) | 一般为函数结束 | 函数参数值,临时变量,局部变 |
堆(heap) | 由程序猿开辟,由程序猿释放或程序结束释放 | 内存的动态存储区,存放程序运行中的临时数据 |
注意:内存的动态分配是向系统申请内存空间,但是由于这个内存空间不像数组一样有数组名,所以对这段内存的读写都是靠指针来实现的;
怎样建立内存的动态分配
系统提供的库函数:
函数名 | 全称 | 功能 |
---|---|---|
malloc(n) | 动态内存分配函数 | 在堆总开辟n个字节的存储空间 |
calloc(n,m) | 动态内存分配并清零函数 | 在堆中开辟n*m个字节空间(),为每个元素清零 |
free§ | 动态内存释放函数 | 释放指针p所指向的开辟的内存空间 |
realloc(p,n) | 动态内存调整函数 | 修改p所指向的动态内存空间为n个字节(p的值不变) |
**注意:**这些函数定义在库函数stdlib.h中,在使用时需要调用其头文件;
动态内存分配函数malloc
函数原型:void *malloc(unsigned int size);
函数特点:
- 函数为无符号指针函数,返回值是不指向任何数据类型的此函数所开辟的内存空间的首地址;
- 参数为无符号整型,因为要开辟的内存空间数不会是负值;
- 如果开辟空间失败(内存不足或其他情况),函数返回值为NULL;
- 此段内存空间中的值为任意值,也就是没有进行初始化,memset函数可以完成对这个空间的初始化;
举例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(100);
printf("%d\n",p);
}
运行结果:(这个地址是随机的,每次运行结果并不相同)
图像表示"p = malloc(100)":
动态内存分配并清零函数calloc
函数原型:void *calloc(unsigned int n , unsigned int size);
函数特点:
- 函数同样为空类型指针类型,返回的地址同样为所开辟空间的首地址,如果开辟失败,则返回NULL;
- 开辟的是一个数组空间,此数组一共n个元素,每个元素的存储字节数为size;
- 除了开辟空间之外,此函数还为每个元素赋初值为0;
- 函数calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零.
举例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = calloc(4,100); //在栈中分配4个长度为100字节的内存空间;
printf("%d\n",p);
}
运行结果:(这个地址是随机的,每次运行结果并不相同)
图片解释“p = calloc(4,100)”:
动态内存释放函数free
函数原型:void free(void *p);
函数特点:
- 此函数用来释放指针p所指针的动态内存空间;
- 一般和malloc函数搭配使用;
- 一般free函数释放的是最近开辟的一个内存空间;
- 当程序运行过程中malloc了,但是没有free的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,使得系统可用内存不断减少.但是内存泄漏仅仅指程序在运行时,程序退出时,OS将回收所有的资源.因此,适当的重起一下程序,有时候还是有点作用.
举例说明:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(4,100);
printf("%d\n",p);
free(p); //需要注意的是,内存释放后,p本身的值并没有发生改变;
}
运行结果:(这个地址是随机的,每次运行结果并不相同)
动态内存调整函数realloc
函数原型:void *realloc(void *p,unsigned int size)
函数特点:
- 函数返回值为指针,如果操作失败(内存不足或其他)返回为NULL;
- 函数用来重新分配由malloc和calloc函数所开辟的首地址为p内存空间,修改其大小为size;
- 重新分配后,p的值可能会发生改变;
- 此函数是堆中开辟的内存;
- realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,问题就出来了,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.这句话传递的一个重要的信息就是数据可能被移动.
- realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,对于缩小,则被缩小的那一部分的内容会丢失.realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.相反,realloc返回的指针很可能指向一个新的地址.
举例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(100);
realloc(p,120);
printf("%d\n",p);
free(p);
}
运行结果:(这个地址是随机的,每次运行结果并不相同)
图片解释“realloc(p,120)”:
但是realloc函数不仅可以增加拓展内存空间,还可以缩短内存空间;
例如:realloc(p,80)代表:
注意:
那么,这些内存的开辟干什么用哪?
用来存临时数据, 而且是一般存储的数据往往都是有一定量的,但是这里有个问题:分配的临时存储空间内并没有数据类型,例如malloc开辟的空间,就只是一个由若干个字节组成的内存空间块,如何像数组一样去存储数据,并按照地址去访问它们?解决这个问题的办法是强制类型转换;并且有一点要说明的是,在C 99之前,malloc和calloc函数的数据类型是字符指针型(char *)的,但即使是这样,这些内存空间并不一定是用来存字符的;
举例说明:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void Chack(int *p);
int i,*p = NULL;
p = (int *) malloc(5*sizeof(int));
/*
1.这里发生了强制性类型转化,但即使不进行强制转化,编译器也会进行默认转化
2.sizeof()函数解决了程序的健壮性和可已知项以及所开辟空间字节数和所转化数据类型的不匹配问题;
*/
printf("请输入5个人的成绩:\n");
for(i=0;i<5;i++)
scanf("%d",p+i);
Chack(p);
free(p);
}
void Chack(int *p)
{
int i=0;
for(;i<5;i++)
if(p[i]<60)
printf("不合格的成绩有:%d\n",p[i]);
}
运行结果:
图片解释:“p = (int ) malloc(5sizeof(int)); ”
显然,所开辟的空间被转换成了数组一样的内存空间,并存储了数据,各个小存储区可以利用指针的算术运算存取;
写博客途中有遇到一些问题,拜读过这位前辈的博客,写的很好,很有深度:
https://blog.csdn.net/shuaishuai80/article/details/6140979