1、问题导向:
在数组的扩容过程中,无论是将新创建的更大的数组返回,不是将形参的指针指向更大的数组,其实都是在主函数中,获得了一个局部变量的地址,所以以上两个操作都是非法的。
以上操作非法的根本原因就是因为用户试图返回一个栈空间的指针,而栈空间的特性就是自动释放。
那么想要完成扩容的操作,我们就不能使用会自动释放的指针,此时我们可以使用堆空间内存来解决这个问题。
2、动态内存管理:
1)、堆空间的特性:
- 随机访问
- 手动申请
- 手动释放
2)、堆空间指针的申请:
函数名:malloc
参数列表: (int size)
函数功能:申请size个大小的连续内存空间,
函数返回值:void * 将申请完成的堆空间指针返回给用户,用户可以使用任意类型的指针去接受。
3)、初始化
malloc
函数申请的堆内存空间,都是未初始化的,如果直接使用,容易造成部分空间依旧是垃圾值。所以,需要对该内存空间进行初始化,使用函数memset;
函数名:memse
参数列表:(void *p,int num,size_t n)
a功能:将p指向的内存空间上的前n个字节全都赋值成num
返回值:将更新后的p返回给外部。
4)、释放手动申请的空间
函数名:free
参数列表(void *p)
函数功能:将p指向的一片连续的堆内存空间释放。
返回值:空
查看内存的状态并运行程序:
valgrind ./a.out
函数名:calloc
参数列表:(int num,size_t size)
功能:手动申请一片堆内存空间,该内存空间大小 为num*size个,size为该指针指向的内存空间的单个大小。
返回值:void *
calloc和malloc的区别:
calloc
比malloc
多一个参数,从申请数据的角度来说,calloc
更直观。calloc
申请出来的内存空间,默认初始化为0;
函数名:realloc
参数列表:(void *prt,size_t size)
功能:为ptr指向的堆内存空间,重新申请大小,其大小为size。
返回值:void *
注意,
多次调用realloc
容易堆积内存碎片,导致意料之外的段错误,所以需要进行扩容时,尽量自己手动编写扩容函数。
当一个指针不是在主函数内申请的,而是在功能函数内申请的时候,如果我需要这个指针在其他任意的函数内能够使用,那么此时,这个指针则需要指向推空间的指针。
刷新printf缓存的3种方式:
- 使用换行符
- 使用
fflush
函数,强制刷新stdout
. - 程序结束,自动刷新缓存。