为什么要有动态内存管理:让程序员自己可以申请和释放空间(数组等开辟空间的大小是固定的)
内存中的不同区
malloc函数
功能:向内存的堆区申请一块连续可用的空间,并返回指向这块空间的起始地址。
1.如果开辟成功,则返回这块空间的起始地址
2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
3.返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己做决定(申请了一块地方,但不知道申请的额这块地方是放什么的,所以返回这块空间的起始地址)
4.如果参数size为0,malloc的行为标准是未定义的,取决于编译器(放0无意义)
需要的头文件stdlib.h
return 1表示malloc开辟失败,程序非正常结束,如果是正常结束,应该执行return0
free的使用格式
free是用来释放动态开辟的内存,free释放了空间,但是指针的值仍在,这是指针就变成了野指针,非常危险,所以需要手动将指针p=NULL(变长数组不能初始化)
calloc函数也用来动态内存分配
函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
与函数malloc的区别在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0
realloc函数
realloc函数的出现让动态内存管理变得更加灵活,realloc函数可以做到对动态开辟内存大小的调整
ptr是要调整的内存的地址;size是调整之后的新大小,单位是字节;返回值为调整之后内存的地址;若原来的空间后的位置已被分配(位置不够),则开辟新的一块空间,并且将旧的空间里的数据重新拷贝一份到新的空间中,返回新的空间的起始地址,释放掉旧的地址;反之则直接扩大原来空间后的位置,并不发生移动;
realloc函数也可以直接用来申请空间,等价于malloc
常见的动态内存的错误
1.对NULL的解引用:因为malloc函数会开辟失败,若不判断是否开辟成功就会出现问题。
2.对动态内存空间的越界访问:实际用的空间大于malloc申请的空间。
3.对非动态开辟内存使用free:如对数组进行free操作.
4.使用free释放动态开辟内存的一部分
free释放空间的时候一定要给这块空间的起始位置
5.对同一块动态内存多次释放:free了一块空间,在并没有给这块空间置为空的时候再次free
6.动态开辟内存忘记释放(谁申请的空间谁释放,不使用的空间及时释放,自己(函数1)不方便释放的空间,要告诉别人(函数2)释放)
题目分析
例题1
崩溃原因:1.str传递给Getmemory时使用的是值传递,形参变量p其实是str的一份拷贝,当我们把malloc申请的空间的起始地址存放在p中时不会修改str,str依然为空指针。所以当Getmemory函数返回后,再去调用strcpy函数需要将“hello world”拷贝到str指向的空间时,程序崩溃;2.因为malloc申请的空间没有free释放,存在内存泄漏
例题2
结果为随机值原因:数组p为局部变量,存放在栈区,出了作用域就销毁了也就意味着str不可以使用它(即使地址被传过去了)。返回栈空间的地址,当找这个地址时它却被销毁了。
修改这道题的方法,在char p【】前加上static,因为static修饰之后将数组存放在内存的静态区
柔性数组
c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组的成员
这样写有时候会报错,可以改成a【】
柔性数组的特点:1.结构体中的柔性数组成员前面必须至少有一个其他成员;2.sizeof返回的这种结构大小不包括柔性数组的内存;3.包含柔性数组的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小