重点学习内容
动态内存管理四大函数
Malloc 内存申请函数
返回值是无类型的指针,指向分配的内存的首地址。申请失败会返回空指针。
malloc返回值是void*类型,使用时需要强制转换成所需类型。
malloc和free匹配使用,但是如果不free释放内存,内存也会在程序运行结束后自动释放。
但是程序如果是不停止的运行的话,则最好使用free搭配来释放内存。
搭配free使用的方式:
释放空间使用free(p);p=null;
因为p指针释放内存后,所指向空间使用权交还给系统,但是原来p指针变量内存储的指向的空间的地址还存在p当中,此时p解引用还是可以访问所指向空间,成为野指针。所以最好在free后给p赋空值。
free释放的内存必须是动态内存分配的内存。
calloc 开辟空间并初始化函数
按照元素个数和大小开辟空间。返回值也是void* 指向开辟好的空间的起始地址。
返回之前会提前把开辟的空间初始化位0,然后再返回。
malloc和calloc的区别就是返回之前是否有初始化开辟的空间。
realloc 动态内存调整函数
当动态分配的内存不够或者过多时,用此函数调整。
内存调整的两种情况:
当追加的连续字节已被占用,则此函数会在堆中找空闲内存,重新分配所需的内存,把原分配内存的内容拷贝到新地址,然后返回新的地址。旧空间不用手动释放,会自动释放。
直接在后面追加内存,并由原地址继续管理。
频繁的malloc会带来效率的下降,因为开辟的空间之间会产生内存碎片,内存碎片未被利用就会浪费,内存利用率下降。并且每一次开辟和释放都需要消耗系统资源。
代码实现扩容的正确方式
为了防止扩容失败后空指针覆盖原指针,所以用新的指针接收realloc的返回值, 并判断是否是空指针,在进行赋值。
realloc当做malloc使用的方式:
内存池概念:
一次性申请一块相对较大的空间,程序自己来维护和使用。
常见的动态内存错误
避免空指针解引用的解决办法
使用内存开辟函数,一定需要对返回值进行检测是否为空指针。
对非动态开辟空间进行free也是错误的
使用free释放一块动态开辟内存的一部分也是错误的
使用free释放动态内存开辟的空间,必须是开辟的空间的首地址,如果一旦指向首地址的指针变量发生变化,free则会编译报错。
对同一块动态开辟的内存进行多次释放也是错误的
解决方法可以是在第一次free后,把p指针赋为空指针,避免成为野指针和二次释放报错。
动态开辟内存后,没有释放内存,产生内存泄露风险。
不用又不释放则会内存泄露。
实例:
函数内创建的形参,函数结束后会释放。str仍是空指针。
柔性数组(一个重点技巧)
举例:
柔型数组的特点
包含柔性数组的结构体,创建变量时不能直接创建,直接创建会忽略掉柔型数组成员。
创建方法含柔性数组的结构体变量 的方法:
两次malloc手动实现柔型数组的实例:
直接创建柔性数组的优势:
柔性数组可以解决在堆中创建带数组的结构体需要两次malloc的问题。减少开辟次数,产生较少内存碎片,提高访问速度。
补充:变长数组概念
变长数组是在c99标准才支持边长数组,并且边长数组并不意味着长度可变,而是数组长度支持使用变量来表示。