我们开辟空间的时候,空间的大小是固定的。
在数组申明的函数,必须要指定数组的长度,它所需要的内存在编译时分配的。
但是如果想要开辟不固定大小的空间,该怎么办?
解决方式就是动态进行内存的分配,即在堆上开辟空间。
本篇博客将介绍如何动态开辟空间。
一、动态内存函数
C语言中与动态内存管理相关的函数,主要有四个malloc、free、calloc和realloc。
1.malloc
void* malloc(size_t size);
该函数是开辟一个大小为size字节大小的空间。
返回值:若开辟失败,则返回一个NULL指针,因此malloc的返回值一定要作检查。若开辟成功,则返回这块空间的指针,因为该指针为void*类型,所以具体使用时可以进行强转。
2.free
void free(void* ptr);
该函数是用来释放ptr指针指向的一块空间的。
如果参数ptr指向的空间不是动态开辟的,则free函数的行为是未定义的。
若果参数ptr是NULL指针,则该函数什么都不做。
一般用了malloc申请空间,在使用之后一定要释放该空间,不然会造成资源泄露。
#include <stdio.h>
#include <stdlib.h>
//malloc和free函数在该头文件里
int main()
{
int num = 0;
scanf("%d", &num);
int* ptr = (int*)malloc(num * sizeof(int));
if (NULL != ptr)
{
for (int i = 0; i < num; ++i)
{
*(ptr + i) = i;
printf("%d ", *(ptr + i));
}
printf("\n");
}
free(ptr);
ptr = NULL;//防止其成为野指针
system("pause");
return 0;
}
最后一定要ptr设为NULL,不然会变成野指针。
3.calloc
void* calloc (size_t num, size_t size);
该函数功能是为num个大小为size的元素开辟一块空间,并且开辟完之后该空间的每个字节都会初始化为0。
其实该函数就是malloc加上初始化。
返回值:若开辟失败,则返回一个NULL指针。若开辟成功,则返回这块空间的指针,因为该指针为void*类型,所以具体使用时可以进行强转。
其开辟完空间,在这个空间使用完之后也一定要调用free函数将其释放掉。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
//calloc和free函数在该头文件里
int main()
{
int num = 0;
scanf("%d", &num);
//int* ptr = (int*)malloc(num * sizeof(int));
int* ptr = (int*)calloc(num, sizeof(int));
if (NULL != ptr)
{
printf("使用之前,会自动初始化:\n");
for(int i = 0; i < num; ++i)
{
printf("%d ", *(ptr + i));
}
printf("\n");
printf("使用之后:\n");
for(int i = 0; i < num; ++i)
{
*(ptr + i) = i;
printf("%d ", *(ptr + i));
}
printf("\n");
}
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
不难发现,该函数作用和malloc一样,只不过比malloc多了一个初始化。
4.realloc
void* realloc (void* ptr, size_t size);
该函数是当我们发现过去申请的空间太小了、或者太大了的时候,可以用该函数对动态开辟内存大小调整。
ptr是要调整的空间的指针。
size是调制之后的新大小。
返回值为调整之后的内存起始位置。
该函数在调整原内存空间大小的基础上,还会将原来内存中的数据移到新的空间中。
realloc在调整内存空间时会出现两种情况:①原有空间之后本身就有足够大的空间,此时只需要在原有内存之后直接追加空间,原来空间的数据不发生变化。 ②原有空间之后没有足够大的空间,此时要在堆上重新找一个适合大小的连续空间进行使用,此情况下函数返回的是一个新的内存地址。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>//ralloc和free函数在该头文件里
int main()
{
int* ptr = (int*)malloc(5 * sizeof(int));
if (NULL != ptr)
{
for (int i = 0; i < 5; ++i)
{
*(ptr + i) = i;
printf("%d ", *(ptr + i));
}
printf("\n");
}
//用realloc扩容
int* ptr2 = NULL;
ptr2 = (int*)realloc(ptr, 6 * sizeof(int));
if (NULL != ptr2)
{
ptr = ptr2;
}
//之前的数据都在
*(ptr + 5) = 5;
for (int i = 0; i < 6; ++i)
{
printf("%d ", *(ptr + i));
}
printf("\n");
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
二、常见的动态内存错误
对NULL指针的解引用
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(2 * sizeof(int));
*ptr = 20;//不判断
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
此时没有对ptr进行判断,则申请失败时ptr值为NULL,那么就会有问题。
对动态开辟空间越界访问
#include <stdio.h>
#include <stdlib.h>//malloc和free函数在该头文件里
int main()
{
int* ptr = (int*)malloc(5 * sizeof(int));
if (NULL != ptr)
{
for (int i = 0; i < 6; ++i)
{
*(ptr + i) = i;
printf("%d ", *(ptr + i));
}
printf("\n");
}
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
上面 *(ptr + 5) 就是越界访问了。
对不是动态开辟的内存使用了free函数进行释放
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 20;
int* ptr = ⅈ
free(ptr);//
ptr = NULL;
system("pause");
return 0;
}
此时程序会崩。
对同一块动态内存多次释放
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(5 * sizeof(int));
if (NULL != ptr)
{
for (int i = 0; i < 6; ++i)
{
*(ptr + i) = i;
printf("%d ", *(ptr + i));
}
printf("\n");
}
free(ptr);
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
此时程序会崩。
使用free函数释放一块动态开辟内存的一部分
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>//malloc和free函数在该头文件里
int main()
{
int* ptr = (int*)malloc(5 * sizeof(int));
++ptr;//ptr不再指向该动态内存的起始位置了。
free(ptr);
ptr = NULL;
system("pause");
return 0;
}
程序依旧会崩。
当我们动态开辟内存空间时,使用完之后忘记使用free函数释放之后就会造成内存泄漏。
三、C语言内存开辟
C语言中程序的内存区域划分
栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于链表。
数据段(静态区)(static):存放全局变量、静态数据。程序结束后由系统释放。
代码段:存放函数体(类成员函数和全局函数)的二进制代码。
————————————————
版权声明:本文为CSDN博主「Sheena爽」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Sheena997/java/article/details/103701171