本文主要介绍了C语言中动态开辟内存的相关函数。
一、动态内存开辟是什么?
例如:
int a = 10;//在栈空间上开辟4个字节空间
char arr[20] = {0};//在栈空间上开辟20个字节的连续空间
上述是我们之前为变量开辟空间的方式。计算机中内存分为几个区,栈区,堆区,静态区,我们使用动态内存函数开辟的空间存放在堆区。
malloc(), calloc(), realloc(),free()等都是动态内存函数。
那么接下来,本文将介绍如何使用动态内存函数。
二、动态内存函数
头文件:#include <stdlib.h>
2.1 malloc()函数
介绍:动态开辟 size 字节大小的空间。
【函数原型】:void* malloc (size_t size);
【参数】:size 表示需要动态开辟内存的大小,单位:字节。
【返回值】:void* 类型的指针,返回动态开辟空间的起始地址。
由于计算机不知道程序员申请size大小的空间是干什么用的,所以返回void* 类型的指针,由程序员自行决定。程序员使用强制类型转换,使开辟的空间转换成自己需要的空间类型。
【如何使用?】
#include <stdio.h>
#include <stdlib.h>
int main()
{
//动态开辟40字节大小的空间
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("动态开辟空间失败:");
}
else
{
//赋值
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
//打印
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);//释放空间,防止内存泄漏
p=NULL;//避免野指针出现
return 0;
}
int* p = (int*)malloc(10 * sizeof(int));
- 10 * sizeof(int):表示malloc()函数动态开辟10个int类型的空间大小。
- (int*):表示强制类型转化,将malloc()返回的地址转换成自己需要类型的地址。
- 使用int* 类型的指针接收
注意:malloc()函数可能会开辟内存失败,因此,我们在使用malloc()函数开辟的空间之前,要先检验其有效性。如果返回值为空指针,表示动态开辟空间失败,后面无法对指针p进行相关操作,如果不为空表示开辟空间成功,可以对这块空间进行操作。
2.2 free()函数
介绍: 释放由malloc(), calloc(), realloc()动态开辟的空间,防止内存泄露。
【函数原型】:void free (void* ptr);
【参数】:void*ptr,任意类型的指针,指针指向需要释放空间的起始地址。
【返回值】:无返回值。注意:free() 函数只能释放动态开辟的内存空间,否则报错。
#include <stdio.h>
#include <stdlib.h>
int main()
{
//动态开辟10个整型大小的空间
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("动态开辟空间失败:");
}
else
{
free(p);//释放空间
p = NULL;//编程好习惯,释放空间后,立即把指针置空,防止野指针的出现
}
return 0;
}
2.3 calloc() 函数
介绍: 动态开辟num个size大小的空间,并把每个字节都初始化成0。
【函数原型】:void* calloc (size_t num, size_t size);
【参数】:2个参数。num 表示需要开辟元素的个数,size 表示每个元素的大小,单位:字节。
【返回值】:void* 类型的指针,返回动态开辟空间的起始地址。
注意:calloc() 和 malloc() 函数的区别在于,calloc()函数对动态开辟的空间进行了初始化,把每个字节都初始化成0。
【函数使用】:和malloc()函数使用一致, 开辟空间后需要先判断是否开辟成功,开辟成功后再使用。
#include <stdio.h>
#include <stdlib.h>
int main()
{
//malloc()动态开辟10个整型大小的空间
int* p1 = (int*)malloc(10 * sizeof(int));
if (p1 == NULL)
{
perror("动态开辟空间失败:");
}
//calloc()动态开辟10个整型大小的空间
int* p2 = (int*)calloc(10, sizeof(int));
if (p2 == NULL)
{
perror("动态开辟空间失败:");
}
free(p1);//释放空间
p1 = NULL;//编程好习惯,释放空间后,立即把指针置空,防止野指针的出现
free(p2);
p2 = NULL;
return 0;
}
接下来我来带大家看一下,malloc()和calloc()函数开辟空间的内存。
我们发现malloc()函数开辟的空间,内存中都是随机值,而calloc()函数开辟的空间,内存中全部为0。说明calloc()函数开辟空间时,会将开辟的空间每个字节全部初始化为0。这也是calloc()和malloc()函数的唯一区别,除此之外,malloc()和calloc()函数功能一致。
2.4 realloc()函数
介绍: 对动态开辟的空间进行调整。可以调大,也可以调小。
【函数原型】:void* realloc (void* ptr, size_t size);
【参数】:2个参数。void* ptr 表示需要调整空间的起始地址。size 表示调整之后的空间大小,单位:字节。
【返回值】:void* 类型的指针,返回调整之后的动态开辟空间的起始地址。
注意:
realloc()函数,将空间调大时,有两种情况:
- 如果需要进行调整的空间 ptr 之后有足够大的空间,则直接在ptr后面扩大空间,返回这块空间原来的起始地址ptr。
- 如果需要进行调整的空间 ptr 之后的空间不足,则realloc()函数将从堆区的其他地方开辟size大小的空间,并将原地址空间 ptr 的数据拷贝到新空间ptr2中, 释放原起始地址(free(ptr),返回新的起始地址ptr2。
【函数使用】
【情况1:】待调整空间之后,有足够大的空间。
#include <stdio.h>
#include <stdlib.h>
int main()
{
//malloc()动态开辟10个整型大小的空间
int* p1 = (int*)malloc(10 * sizeof(int));
if (p1 == NULL)
{
perror("动态开辟空间失败:");
}
//扩大p1空间, 使空间大小为80个字节 -- 20 * sizeof(int)
int* tmp = (int*)realloc(p1, 20 * sizeof(int));
if (tmp == NULL)
{
perror("realloc fail:");
}
else
{
p1 = tmp; //将扩大后的空间赋值给p1
free(p1);//释放空间
p1 = NULL;//编程好习惯,释放空间后,立即把指针置空,防止野指针的出现
}
return 0;
}
【情况2:】待调整空间之后,空间不足。
#include <stdio.h>
#include <stdlib.h>
int main()
{
//malloc()动态开辟10个整型大小的空间
int* p1 = (int*)malloc(10 * sizeof(int));
if (p1 == NULL)
{
perror("动态开辟空间失败:");
exit(-1);
}
//扩大p1空间, 使空间大小为 9999* sizeof(int) 字节
int* tmp = (int*)realloc(p1, 9999 * sizeof(int));
if (tmp == NULL)
{
perror("realloc fail:");
exit(-1);
}
else
{
打印,验证是否realloc()成功之后是否free()了原地址
//int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%d ", *(p + i)); //编译器报错
//}
p1 = tmp; //将扩大后的空间赋值给p1
free(p1);//释放空间
p1 = NULL;//编程好习惯,释放空间后,立即把指针置空,防止野指针的出现
tmp = NULL;
}
return 0;
}
【这里显示:realloc()从堆区其他地方,开辟size大小空间】
由内存图观察到,p1 和 tmp 的地址值不同。说明p1之后有其他数据,不能直接在后面扩大空间,需要在堆区另外找一块size大小内存,并将原空间p1的数据拷贝到新空间tmp,释放原空间p1,并返回新开辟空间tmp的起始地址
结语
理解malloc(),calloc(),realloc(),free()函数的使用,理解他们开辟空间的过程,以及动态开辟空间如果不再使用,要及时free()掉,防止内存泄漏。
希望以上可以帮助到大家~