C语言学到这里的时候,我们掌握的内存开辟有二种
int a = 10;
int arr[10] = {0};
开辟空间
开辟连续的空间
但是上面的二种方法有二个特点:
但是有时我们在程序还没运行时,不太清楚要多少空间
如果开辟的空间过小,那程序会越界
如果开辟的空间过大,那也会造成空间的浪费
这时后就会使用到动态内存函数
,因为动态内存函数
是在堆区
创建的
栈区
创建的空间大小都是固定的,是不可以更改的
动态内存函数的头文件是
melloc 函数的参数:
void* malloc(sizt_t size);
size_t size 参数,指定要开辟多少个字节的空间,是以字节为单位的
这个函数的作用是向内存申请一块空间,并返回指向这块空间的地址
NULL
malloc
的返回值是 void*
,所以malloc
函数并不知道要开辟什么类型的空间,所以要自行的决定malloc
的参数 size
是 0 ,这种行为是标准为定义的,最后的结果取决于编译器malloc 函数的使用:
#include //动态内存管理对应头文件
int main()
{
// 用 malloc 开辟 40 个字节的空间,然后把空间的起始地址返回给 p
int* p = (int*)malloc(40);
//malloc申请空间可能会失败,所以要进行判断
//申请失败:打印错误信息并退出
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
// 打印
for (i = 0; i < 10; i++)
{
p[i] = i;
printf("%d ", p[i]);
}
free(p); ///释放动态内存开辟的空间
p = NULL;//将p置空,防止成为野指针
return 0;
}
free 函数的参数:
void free(void* ptr)
free
函数是专门用来做动态内存的释放和回收的
free
函数的参数是 ptr 指向的空间并不是动态内存函数开辟的,那 free
是不能进行释放的free
函数是 NULL
,那 free
是什么都不做free
是用于动态内存函数的释放(malloc、calloc、realloc
);calloc 函数参数:
void* calloc(size_t num, size_t size);
calloc
第一个参数是要开辟多少空间calloc
第二个参数是开辟是什么类型的空间calloc
函数在开辟好空间后会自动把所有初始化为0
calloc 函数的使用:
calloc
和 malloc
的差别:
malloc
开辟好空间是不会初始化
的,而calloc
是开辟好空间后会自动把所有初始化为0
#include //动态内存管理对应头文件
int main()
{ // 用 calloc 开辟 10 个空间,大小为 int
int* p = (int*)calloc(10,sizeof(int));
if (p == NULL)
{ //打印错误信息
printf("%s\n", perror);
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
free(p);//释放
p = NULL;
return 0;
}
代码结果:
realloc 函数参数:
void* realloc(void* ptr, size_t size);
realloc 函数是调整通过动态内存开辟的空间
调整的空间的起始地址
未增加空间的大小+想增加的大小的总空间大小
realloc 是以字节为单位的
, 原本空间是 10*4 = 40 个字节,如果想要在增加 10 个int 类型的空间realloc 函数的使用:
int main()
{ // 使用 malloc 开辟 5 个为 int 的空间
int* p = (int*)malloc(5 * sizeof(int));
if (p == NULL)
{ // 判断
printf("%s\n", perror);
return 1;
}
// 使用 realloc 在扩容 5 个空间,总空间大小是 10
//防止p指向realloc开辟失败的空间时,丢失原来空间地址的情况,
//所以使用临时变量接受 realloc 的返回值
int* ptr = realloc(p, 10 * sizeof(int));
if (ptr == NULL)
{ //判断
printf("%s\n", perror);
return 1;
}
// 成功后在赋值给 p
p = ptr;
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i;
printf("%d ", p[i]);
}
free(p);//释放
p = NULL;//将p置空,防止成为野指针
return 0;
}
代码结果:
使用 realloc 时还需要注意realloc在调整内存空间的时候存在两种情况:
realloc 的工作原理第一种情况:
realloc 进行扩容时,原来后面的空间不足以进行扩容
realloc 会找一块 扩容后总空间的大小,然后把原空间的数据拷贝过去,返回的是新空间的地址
旧的空间是会自动销毁
realloc 第二种情况:
realloc 进行扩容时后面的空间充足,是直接在后面开辟空间
malloc、calloc、realloc
这些函数向内存申请空间是有可能会失败的,申请失败函数就会返回空指针,如果我们不对函数的返回值进行判断,而直接对其解引用的话,就会造成程序崩溃;
解决办法:在使用动态内存管理函数申请动态内存时,一定要记得用if
进行检查函数的返回值是否为空。
动态内存开辟的空间是不能随意越界的,动态内存开辟的空间也是有限的
对 free
函数是专门用于释放动态开辟的空间的
如果对非动态开辟的空间进行 free 操作,会造成程序崩溃
用 free
必须是使用 起始地址 释放
当我们成功开辟一块动态空间并将它交由一个指针变量来管理时,我们可能会在后面的程序中让该指针变量自增,从而让其不再指向该动态空间的起始位置,而是指向中间位置或者结尾,这时我们在对其进行 free
进行释放,也会导致程序崩溃,因为free函数必须释放一整块动态内存,而不能释放它的一部分。
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*p = i;
p++; //指针变量p自增导致其丢失动态内存的起始地址
}
free(p);
p = NULL;
return 0;
}
运行结果:
解决办法:将申请的动态内存交由两个指针变量进行管理,其中一个用于各种操作,另外一个用于记录空间的起始地址
我们在写程序的时候可能在程序中的某一位置已经对动态内存进行释放了,但是随后写代码中,我们可能忘记了而重复对一块动态内存进行释放
int main()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
return 1;
}
free(p);
// p = NULL;
//.......
free(p);//重复释放
}
解决办法:进行 free 释放后,把指针置成空指针(NULL),这样即使后面重复释放,free(NULL) 也没有任何影响
内存泄漏是:用动态内存函数申请空间使用完毕后,没有进行回收(free
)
void test()
{
//这种开辟空间 和 malloc 功能是一样的
int* p = (int*)realloc(NULL, 40);
int a = 0;
scanf("%d", &a);
if (a == 5)
return;
free(p);
p = NULL;
}
int main()
{
test();
return 0;
}
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str); //将str的首地址传给printf函数,可行
}
int main()
{
test();
return 0;
}
题解:
GetMemory
函数时,传的是形参,p
只是str
的一份临时拷贝,所以对 p 的修改不会影响 str
GetMemory
函数里p
进行申请空间使用后,并没有进行释放,会导致内存泄漏p
是形参没有对str
进行修改,所以str
还是NULL
,所以在解引用时程序会崩溃char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
GetMemory()
内部创建临时变量,他的作用范围只在在该函数内存GetMemory()
返回了 p 的地址,但是 p 以经销毁了,str 就是野指针void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
return 0;
}
free
,造成了内存泄漏void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
free
掉动态开辟的内存之后没有把相应的指针变量置空,导致if
条件成立,造成野指针问题。