动态内存管理
int n = 10; int arr[20];
char c = 'W'; char ch[20];
一个元素 一片空间,上面两种一旦申请成功,空间大小就无法调整
动态内存,是可以根据情况进行内存大小修改的
下面是4个内存函数
malloc
free
calloc
realloc
4个函数头文件都是 <stdlib.h>
4个函数所操控的空间都在堆区上
malloc
malloc的原型:
void* malloc(size_t size)
size 为你要想申请的空间大小,单位为字节
void* 这个函数向内存申请一块联系可用的空间,
成功返回这片空间的起始位置
失败返回 NULL
free
free的原型:
void free (void* ptr)
ptr为你向释放空间的起始地址
如果传参为 NULL free函数什么也不会做
专门用来做动态内存的释放和回收的,如果不释放,程序在运行时这片内存一直是你的
free(p); free释放了p指向的空间后p会成为野指针,所以要尽早赋值 NULL
p = NULL;
calloc
calloc的原型:
void * calloc(size_t num, size_t size);
作用:为 num 个大小 size 的元素开辟一片空间,并且把空间的每个字节初始化为0
calloc 和 malloc 的区别点就两
1.原型传参的不同
2.calloc多了给空间初始化的功能
realloc
realloc的原型:
void realloc (void* ptr, size_t size)
void* 为申请调节空间的起始地址
size 为准备申请调整成 size 大的空间,大小为字节
realloc 是动态内存的关键
realloc 在开辟空间会有3种情况:
1.调整失败返回 NULL
所以,常常在使用玩后会追加判断,以避免使用 realloc 后返回NULL
2.3.情况如图

2.为连续空间开辟 后续空间足够
3.为连续空间开辟 后续空间不足
情况3 .会在空间中再找一块足够的内存空间,将原来空间的数据拷贝一份到新的空间,然后释放旧的空间,返回新的空间起始位置
有类写法,可以将 realloc 的作用和 malloc 一样
realloc(NULL,20); 等于 malloc(20);
给 realloc 传NULL,realloc会因为找不到地址,重新开辟一块空间
常见的动态内存错误
1.对NULL指针解引用操作
cpp
int *p =(int*)malloc(int_MAX);
*p = 20; // 报错,p有对NULL进行解引用的风险
free(p);
p = NULL;
2.对动态内存开辟空间的越界访问
cpp
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
}
free(p);
}
3.对非动态开辟内存使用free
cpp
int a = 10;
int* p = &a;
free(p);
P = NULL;
4.使用free释放动态内存空间的一部分
cpp
int *p = (int *)malloc(100);
p++;
free(p) // P 已经不是内存的起始位置了
5.对同一块动态内存多次释放
cpp
int *p = (int *)malloc(100);
free(p);
free(p);// 重复释放
6.开辟的动态内存忘记释放(内存泄漏)
当程序结束时,内存会由操作系统回收,但有程序7 * 24小时运行(游戏)
cpp
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
问题1:下方代码哪里有问题
cpp
void GetMemory(char *p) // 2.p 为新创建的指针变量 内存中存着 NULL
{
p = (char *)malloc(100); // 3.开辟的内存没有传递过去,也没有释放,导致内存泄漏
}
void Test(void)
{
char *str = NULL;
GetMemory(str); // 1.指针变量 本质还是变量,所以这是传值调用
strcpy(str, "hello world"); // 4.str 为空指针,对空指针进行解引用导致程序崩溃
printf(str);
}
**问题2:**下方代码哪里有问题
cpp
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory(); // 成功返回数组p的地址,但是指针指向的空间已被程序回收
printf(str); // str为野指针 回收的空间内容可能会被覆盖,可能未被覆盖
}
柔性数组:
结构体中的最后一个元素是未知大小的数组,就叫做柔性数组
达成条件:
1.结构体中最后一个数组
2.数组未指定大小
柔性数组的两种写法:
cpp
// 1
struct S
{
int a;
int arr[];
};
// 2
struct S
{
int a;
int arr[0];
}
特点:
1.结构体中柔性数组成员前必须至少有一个其他成员
2.sizeof 在计算结构体内存大小时不会包括柔性数组
3.包含柔性数组成员的结构用malloc函数进行的动态分配,并且分配的内存大小应该大于结构的大小,以适应柔性数组的大小
柔性数组的使用:
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
struct S
{
int a;
int arr[];
} * p;
int main()
{
struct S* ptr = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
if (ptr == NULL)
{
perror("ptr");
return 1;
}
p = ptr;
ptr = NULL;
for (int i = 0; i < 10; i++)
*(p->arr + i) = i;
for (int i = 0; i < 10; i++)
printf("%d ", *(p->arr+ i));
free(p);
p = NULL;
return 0;
}