目录
一、为什么存在动态内存分配
我们已经掌握的内存开辟方式有:
int a = 20;//在栈空间上开辟四个字节
char arr[10] = {0;//在栈空间上开辟十个字节的连续空间
上述开辟内存的方式有两个特点:
(1)空间开辟大小是固定的
(2)数组在申明时,必须指定数组长度,他所需要的内存在编译时分配
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组编译时开辟空间的方式就不能满足了。这时候就只能试试动态内存开辟了。
二、动态内存函数介绍
1.malloc 和 free
C语言提供的动态内存开辟函数
这个函数所需头文件:#include <stdlib.h>
void* malloc (size_t size);
(1)如果开辟 成功 ,则返回一个指向开辟好空间的指针。(2)如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。(3)返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。(4)如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
void free (void* ptr);
free 函数用来释放动态开辟的内存
malloc申请的内存空间,当程序退出时,还给操作系统。当程序不退出时,内存动态申请的内存,不会主动释放。需要使用free函数来释放。
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num = 0;
scanf("%d ", &num);
int arr[num] = { 0 };
int* ptr = NULL;
ptr = (int*)malloc(num * sizeof(int));
if (ptr != NULL)//判断ptr是否为空指针
{
int i = 0;
for (i = 0; i < num; i++)
{
*(ptr + 1) = 0;
}
}
free(ptr);//释放掉ptr所指向的动态内存
ptr = NULL;//释放完置空是好习惯!
return 0;
}
2.calloc
C语言还提供了个函数叫calloc,也是用来动态内存分配,原型如下:
void* calloc (size_t num, size_t size);
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr = (int*)calloc(10 , sizeof(int));
if (ptr == NULL)
{
perror("calloc");
return;
}
//打印数据
for (int i = 0; i < 10; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
ptr = NULL;
return 0;
}
所以如何我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。
3.realloc
void* realloc (void* ptr, size_t size);
开辟新空间会将旧空间中的数据拷贝到新空间释放旧空 间返回新空间的起始地址
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return;
}
//初始化
for (int i = 0; i < 10; i++)
{
p[i] = i;
}
int* ptr = (int*)realloc(p, 60);
if (ptr != NULL)
{
p = ptr;
}
else
{
perror("realloc");
return;
}
for (int i = 0; i < 20; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
三、常见的动态内存的错误
1.对NULL指针的解引用操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
2.对动态开辟空间的越界访问
int main()
{
int* ptr = (int*)malloc(10 * sizeof(int));
if (ptr != NULL)//判断ptr是否为空指针
{
int i = 0;
for (i = 0; i <= 10; i++)//越界访问
{
*(ptr + 1) = i;
}
}
else
{
perror("malloc");
return;
}
free(ptr);//释放掉ptr所指向的动态内存
ptr = NULL;//释放完置空是好习惯!
return 0;
}
3.对非动态开辟内存使用free释放
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 0;
int* p = &a;
free(p);//这里p不是动态开辟内存空间
//动态内存开辟的空间都是在堆区上进行开辟的,而变量a是在栈区开辟的,因此并不能让free来释放
return 0;
}
4.使用free释放一块动态开辟内存的一部分
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return;
}
for (int i = 0; i < 5; i++)
{
*p = i;
p++;//这里p不在指向动态内存的起始位置,p指针的位置发生了变化
}
free(p);
p = NULL;
return 0;
}
5.对同一块动态内存多次释放
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return;
}
free(p);//p = NULL;能避免多次释放所带来的问题
free(p);
return 0;
}
6.动态开辟内存忘记释放(内存泄露)
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
//free(p);
//p = NULL;
//一定要释放动态开辟内存空间,并且正确释放
}
int main()
{
test();
return 0;
}
四、经典例题!!!
1.空指针 内存泄漏
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
(1)函数GetMemory属于传值调用,对形参p的修改并不会影响到实参str,形参p调用完之后会自动销毁,所以str的值仍为NULL;
(2)没有对p进行释放,会造成内存泄漏,malloc分配的内存空间的地址存放在形参p中,所以当形参p销毁之后,也就没人知道malloc所分配的内存空间的起始地址,因而也就无法释放,最终会造成内存泄漏。
//改正
#include <stdio.h>
#include <stdlib.h>
void GetMemory(char** p)
{
*p = (char**)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
2.返回栈空间问题
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
(1)p是数组名,是一个临时变量,表示的是数组首元素的地址。而局部变量是在栈区开辟,函数调用完之后就会自动销毁.
(2)此时将变量p赋值给变量str,而变量p已经变成野指针,会造成非法访问内存;
(3)返回栈空间地址的问题:栈空间的地址不要随意返回,会产生野指针。
char* GetMemory(void)
{
char* p = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
3.未释放(内存泄漏)
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
//改正
//free(str);
//str = NULL;
}
4.指针未置空
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
// str = NULL;
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
str依旧保存着所开辟空间的起始地址,这将导致指针悬空。
五、C\C++程序的内存开辟
本篇博客就到这啦!希望能帮到大家!有错误希望指出!