写在前面
本篇文章为动态内存函数的使用详解,希望对你的动态内存函数学习有所帮助。
为什么需要使用动态内存
对于初学者来说,最先接触到的内存使用便是以下场景:
代码语言:javascript复制//
int val = 3;//为变量val在栈区上申请一块空间存储数据
char str[] = "abc";//为数组str在栈区上申请一块空间存储数据
这样的空间开辟方式,在后续操作中,是无法改变以上数据所占空间大小的,并且对于数组来说,开辟空间是必须指明数组长度的。而在我们实际生活中又确实会出现一组数据量会随时变化的数据组。这时我们就需要使用动态内存函数来为数组,变量来开辟空间。
动态内存函数
(函数声明在头文件stdlib.h
中)
malloc和free
malloc
是C语言提供的一个开辟动态内存的函数。
void* malloc (size_t size);
这个函数向内存申请一块在堆区上连续可用的空间,并返回指向该空间的指针。
- 开辟成功会返回指向开辟好的空间的指针,失败则返回NULL指针。
- 返回值的类型是
void*
指针,具体使用时只需要对返回的指针进行强制类型转换即可。 - 在标准中
malloc
并未对size
是0的情况进行规定,具体情况看编译器。
同时,C语言提供另外一个函数free
,专门用于释放和回收动态内存。
void free (void* ptr);
free
函数接收一个指向一块开辟好的动态内存空间,释放并回收这块J空间。
- 标准对参数
ptr
指向的空间不是动态开辟的这个行为并没有做出规定。 - 如果接收到的
ptr
是NULL指针,函数不会进行任何操作。
int main()
{
int n = 10;
int* array = (int*)malloc(sizeof(int) * n);//开辟n个整型数据大小的连续空间
if (array == NULL)//检测是否申请失败
{
perror("malloc failed");//发出失败提示
exit(-1);//运行失败,结束程序
}
for (int i = 0; i < n; i )
{
array[i] = i;//此时当作数组使用
}
free(array);//释放动态内存
array = NULL;//对该指针置空,防止非法访问内存空间(野指针)
return 0;
}
calloc
除malloc
外,C语言还提供了一个函数calloc
用于动态内存分配。
void* calloc (size_t num, size_t size);
- 函数的功能是开辟
num
个大小为size
的空间 - 与
malloc
不同的是,calloc
会将申请到的空间的每个字节初始化为0
int main()
{
int n = 10;
int* array = (int*)calloc(n, sizeof(int));//申请n个整型大小的内存空间
if (array == NULL)//检测是否申请失败
{
perror("calloc failed");//发出失败提示
exit(-1);//运行失败,结束程序
}
for (int i = 0; i < n; i )
{
printf("%d ", array[i]);//此时打印,是已初始化的数据,全零
}
printf("n");
free(array);
array = NULL;
return 0;
}
(代码运行截图)
realloc
仅有以上的函数要实现真正的动态地使用一块内存空间还是不够的。以上函数功能仅仅是申请和释放一块动态内存,而我们还需要一块改变动态内存大小的函数,这个函数就是realloc
。
void* realloc (void* ptr, size_t size);
-
ptr
指向需要调整的内存空间的地址。 -
size
是调整之后的大小。 - 返回值为一个指向调整之后的空间起始地址的
void*
的指针。 - 如果申请失败会返回一个空指针,并且不会自行释放原先的空间。
-
realloc
在调整内存空间大小时存在两种情况:- 一:在原有空间之后又足够大的空间(即没被其他数据占用)。 这种情况直接原地扩容,追加原有数据后方的空间且不对原有数据做出改动。
- 二:原有空间之后空间不够大。
这种情况
realloc
函数会在堆的其他位置上找一块总够大的空间,将原有数据拷贝进去,并且会自行释放原来占用的空间,最后返回的地址是一个新的地址。
int main()
{
int n = 10;
int* array = (int*)calloc(n, sizeof(int));//申请n个整型大小的内存空间
if (array == NULL)//检测是否申请失败
{
perror("calloc failed");//发出失败提示
exit(-1);//运行失败,结束程序
}
//危险的操作
//array = (int*)realloc(array, 12);//由于申请失败时不会自行释放原空间,而此代码将原先指向原空间的指针置空,无法再找回原空间并释放(内存泄露)
//安全的操作
int* ptr = NULL;
ptr = (int*)realloc(array, 12);
if (ptr != NULL)//检测是否扩容失败
{
array = ptr;//扩容成功再赋值回来
}
//...
free(array);
array = NULL;
return 0;
}
动态内存函数常见使用错误
由于动态内存函数地使用涉及指针,内存空间的知识,对于C语言这块内容还不是很熟悉的人来说使用难度较大。这里总结几个比较常出现的错误,希望对你的使用有所帮助。
对NULL指针的解引用
代码语言:javascript复制void test1()
{
int* ptr = (int*)malloc(sizeof(int));
//如果malloc申请空间失败那么此时ptr就是NULL
*ptr = 9;//此时就会发生
}
对动态开辟空间的越界访问
代码语言:javascript复制void test2()
{
int* ptr = (int*)malloc(10 * sizeof(int));
if (ptr == NULL)
exit(-1);
for (int i = 0; i <= 10; i )
ptr[i] = i;//当i==10的3时候发生越界
free(ptr);//值得注意的是,动态内存空间的越界并不会直接检测出来,而是会在free的时候检测出来并报错
//此时会报出类似堆区异常访问,或者在访问正常数据后的空间之类的错误
}
(代码运行截图)
使用free
释放非动态开辟的内存空间
代码语言:javascript复制void test3()
{
int a[10] = { 0 };
int* p = &a;
//...
free(p);//报错
}
对同一块动态内存空间多次释放
代码语言:javascript复制void test4()
{
int* ptr = (int*)malloc(sizeof(int));
if (ptr == NULL)
exit(-1);
//...
free(p);
free(p);//重复释放
}
只释放一部分动态内存空间
代码语言:javascript复制void test5()
{
int* ptr = (int*)malloc(10 * sizeof(int));
if (ptr == NULL)
exit(-1);
//...
p ;
//...
free(p);//只释放了一部分内存
}
忘记释放动态内存空间(内存泄漏)
代码语言:javascript复制void test6()
{
int* ptr = (int*)malloc(10 * sizeof(int));
if (ptr == NULL)
exit(-1);
//...
//内存泄露
}
结语
非常感谢各位读者能读完这篇文章,如果你觉得做的还不错的话,可以点赞收藏分享,让更多的朋友知道,当然,如果你觉得有什么问题的话也欢迎在评论区留言或私信告诉我哦!下期再会!
彩蛋
源码在这: gitee-test分支-动态函数详解文件 GitHub-master-Dynamic memory.c