传统数组的缺点
数组长度必须事先制定,且只能是常整数,不能是变量(c99可以是变量)
传统形式定义的数组,该数组的内存程序员无法手动释放
在一个函数的运行期间,系统为该函数分配的空间会一直存在知道该函数运行完毕时,数组空间才会被系统释放
数组的长度一旦定义,其长度就不能再修改了,数组的长度不能在函数运行的过程中动态的扩充或缩小
函数在运行期间可以被其他函数使用,但是函数运行完毕后,函数中的数组将无法再被其他函数使用
静态内存和动态内存比较
静态内存是由系统自动分配,由系统自动释放的
动态内存是由程序员手动分配,手动释放,动态内存不会因为函数的释放而被释放,它不手动释放则会一直存在
为什么存在动态内存分配
一般情况下, 空间开辟大小是固定的(例如只要定义一个int类型变量,它的大小在一般环境下就是4字节)
数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配,这个数组如果没有用完会浪费,用完了的话则可能会不够
动态内存分配可以根据我们所需要的多少进行内存的开辟,可以减少内存浪费和开辟空间不足的问题
malloc函数
malloc函数可以向内存申请一块连续可用的空间,并返回这块空间的内存地址
返回值是这块空间的内存地址,其实也就是这块空间的起始地址
写法:malloc(要开辟空间的字节数)
如果开辟成功,则返回一个指向开辟好空间的指针
如果开辟失败,则返回一个NULL,因此malloc的返回值一定要做检查
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,需要我们自己强制类型转换指针来定义类型,例如 int*p = (int*)malloc(40)
如果要开辟空间的字节数为0,malloc的行为是标准是未定义的,取决于编译器
头文件<stdlib.h>
free函数
free函数用来释放动态开辟的内存
写法:free(动态内存指针名)
这个动态内存指针名其实也就是动态开辟的这块空间的起始地址,如果此时动态内存指指向的不是起始地址,则在移动之前需要进行地址的保存,将起始地址赋值给另一个指针
释放动态开辟的内存后,最好把动态内存指针赋值成空指针NULL
如果参数指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数是NULL指针,则函数什么事都不做
头文件<stdlib.h>
当我们不释放动态申请的内存的时候,如果程序结束,动态申请的内存由操作系统自动回收,如果程序不结束,动态内存是不会自动回收的,就会形成内存泄漏的问题
calloc函数
calloc函数可以向内存申请一块连续可用的空间,并将这块空间的每个字节全部初始化为0
写法:calloc(元素个数,每一个元素的字节大小)
如果开辟失败,则返回一个NULL
返回值是指向这块空间的指针,其实也就是这块空间的起始地址
返回值的类型是 void* ,所以calloc函数并不知道开辟空间的类型,需要我们自己强制类型转换指针来定义类型,例如 int*p = (int*)calloc(10,sizeof(int))
realloc函数
realloc 函数可以对动态开辟内存大小的调整
写法:realloc(动态内存指针名,调整后的空间字节大小)
返回调整后动态内存的内存地址,也就是它的起始地址
如果开辟失败,则返回一个NULL
这个动态内存指针名其实也就是动态开辟的这块空间的起始地址,如果此时动态内存指指向的不是起始地址,则在移动之前需要进行地址的保存,将起始地址赋值给另一个指针
如果原空间之后有足够的连续空间扩容时,则直接开辟原空间之后的内存空间扩容
如果原空间之后没有足够的连续空间扩容时,则会重新开辟一块调整后空间大小的内存空间,将原空间的数据全部放到新开辟的空间中去,同时free掉原空间,再同时返回这个新开辟空间的内存地址,也就是它的起始地址
返回值的类型是 void* ,所以realloc函数并不知道开辟空间的类型,需要我们自己强制类型转换指针来定义类型,例如 int*p = (int*)calloc(x,80)