1 为什么会存在动态内存分配
有时候需要的空间大小只有在程序运行时才能知道。例:
char name[10] = { 0 };
scanf("%s",name);
//1 若用户只输入了lily,只需用5个空间来存储该名字(包括'\0'),就浪费了5 byte
//2 若用户输入的内容超过数组大小,也会产生问题
所以需要动态的内存管理。
2 动态内存管理的相关函数
malloc函数
原型:
void* malloc(size_t size)
功能:
向堆区(内存中专门用来动态内存管理的空间)申请size个字节的空间,
申请成功返回该空间的起始地址(void*类型);.
申请失败返回NULL(没有足够空间).
使用:
申请示例:
int* pa = (int*)malloc(4*sizeof(int));
//申请16个字节的空间,来存放整数
//但由于返回的是void*类型指针,所以强制类型转换成(int*),用int*指针类型接收
if (pa == NULL)
{
printf("%s", strerror(errno));//判断是否是空指针,并打印出空指针原因
return 0;
}
使用示例:
//通过pa指针变量来使用堆区申请的空间
for (int i = 0; i < 4; i++)
{
//让这块空间存放 0 1 2 3
*(pa + i) = i; //或pa[i] = i
}
for (int i = 0; i < 4; i++)
{
printf("%d ", *(pa + i));
}
注意:
(1)当这块空间使用完后,要使用free函数释放这块空间!!!
(2)如果size为0,则是标准未定义的行为
(3)返回值的类型是void*,具体要怎么使用这块空间取决于使用者。
free函数
原型:
void free(void* ptr) //无返回值
功能:
用来释放动态开辟的内存空间,ptr是之前动态开辟的空间的首地址。
为什么要释放?
开辟的动态内存若使用完且不需要再使用,不free:
程序结束,动态申请的内存由操作系统自动回收;
程序不结束(一直运行或长时间运行),申请的内存不使用,就会导致内存泄漏(空间浪费)。
所以要养成用完动态内存后free的习惯
使用:
使用示例:(接上面代码)
free(pa);//释放完这部分动态内存后,这部分空间属于操作系统,不能非法访问
pa = NULL;//但pa指针仍然指向这里的首地址,pa会变成野指针,所以free后应给它赋空指针
注意:
(1) ptr是之前动态内存开辟的首地址.
(2) 如果ptr不是动态开辟的地址,则free的行为是未定义的.
(3) 如果free(NULL),什么都不会发生.
calloc函数
原型:
void* calloc(size_t num, size_t size)
功能:
向堆区申请num × size个字节空间,需要传2个参数,每个元素类型的大小 和 元素数目;
(与malloc函数类似)
但还会把这块空间的内容全部初始化为0.
使用:
使用示例:
realloc函数
原型:
void* realloc(void* memblock, size_t size)
功能:
个人认为这是动态内存管理的灵魂函数。。。。
有时我们发现过去申请的空间小了(或过大了),此时realloc可以对之前申请的动态空间做调整。
扩容的两种情况:
memblock是之前开辟的动态内存的起始地址,会把这个空间大小改成size个字节.
1 若后面有足够的未使用空间:
就直接追加到后面,从而达到扩容的效果,返回原来的起始地址.
2 若后面的空间某些地方被使用:
realloc会在内存重新找一块区域,并把之前的数据拷贝一份放在前面,返回新的起始地址。
另外,它还会自动free之前的动态空间
使用:
char* arr = (char*)calloc(10, sizeof(char));
if(arr == NULL)
{
perror("calloc");//如果申请失败,结束main函数并打印失败原因
return 0;
}
scanf("%s", arr);
if ((int)strlen(arr) < 5)
{
//要注意'\0'也要存的
//如果用户输入的字符串长度太短,把动态内存调小
char* tem = realloc(arr, 5);//先判断是不是空指针
if (tem == NULL)
{
perror("realloc");
return 0;
}
else
{
arr = tem;
printf("%s", arr);
}
}