C语言动态内存管理常用函数
动态分配是在C语言学习中是特别重要的比如以下两行静态内存的代码
int a=20; //开辟了四个字节
char arr[10]={0}; //开辟了10个连续的字节空间
但是静态开辟的空间有两个特点:
(1)空间大小是固定的。
(2)数组在申请的时候,必须指定数组的长度,数组空间一旦确定了就不能调整大小。
但是对于空间的需要,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟的空间的方式就不能满足了。所以C语言引入了动态内存开辟,让程序员可以申请和释放空间,就比较灵活了。
在C语言中常使用于的动态内存开辟的函数是malloc,calloc,realloc,free。malloc和free,calloc,realloc都声明在 stdlib.h 头⽂件中。
一,malloc
malloc函数的使用方式为:void*malloc(size_t size)
void*:这个表示这个可以是任何的类型,比如int* ,char*,这个类型是由使用者自己定义的。
size_t size:表示内存分配的大小单位是字节。
这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针,但是要注意以下几点
(1) 如果开辟成功,则返回⼀个指向开辟好空间的指针。
(2)如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
(3) 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃ ⼰来决定。
(4)如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
二,free
C语⾔提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的。
free函数的使用方式为:void free(void*ptr)
void:在真正的使用过程中void是不需要写的,它这是单纯的表示任何类型的动态内存开辟的空间都可以使用它来释放。
void* ptr:表示然后类型的指针都可以。
free函数⽤来释放动态开辟的内存。但是要注意以下几点
(1)如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
(2)如果参数 ptr 是NULL指针,则函数什么事都不做。
举个例⼦:
#include<stdio.h>
#include<stdlib.h>
int main()
{
//注意:int*p=(int*)malloc(5*sizeof(int))==int* p = (int*)malloc(20)
int* p = (int*)malloc(20); //20个字节存放5个整数
if (p == NULL) //判断动态内存分配的空间是否成功
{
perror("malloc");
return 1;
}
//使用空间
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
free(p);
p = NULL;
return 0;
}
通过这张图片我们可以很明确的看到通过代码申请了5个整形空间。
然后通过循环函数进行相应的赋值。
然后这里是i等于5离开了循环函数进入了free函数对动态开辟的空间进行释放和指向NULL。
注意:对动态内存开辟的空间使用free函数释放后比须对释放的空间进行指NULL,因为你对那块空间进行释放后等于把这块空间的操作权限还给了操作系统,但是指针还会指向这块开辟的空间,这样将会把这块空间变成野指针,使用动态内存开辟的空间被free函数释放后必须将这块空间指NULL。
三,calloc
C语⾔还提供了⼀个函数叫 calloc , calloc 函数也⽤来动态内存分配
calloc函数的使用方式为:void calloc (size_t num, size_t size)
void*:这个表示这个可以是任何的类型,比如int* ,char*,这个类型是由使用者自己定义的。
size_t size:表示内存分配的大小单位是字节。
但是要注意以下几点
(1)函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
(2)与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全
0。
举个例⼦:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(5, sizeof(int));
if (NULL != p)
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
通过调试可以明确的看到在使用看到在使用calloc函数进行开辟空间成功之后,然后直接对开辟的空间进行了初始化为0.
通过上图可以看到然而对比使用malloc函数开辟的空间(cd表示随机值),malloc函数则不会对开辟的函数进行初始化,calloc函数会对开辟的空间进行初始化这是malloc函数对比calloc函数最明显的区别。
四,realloc
realloc函数的出现让动态内存管理更加灵活。有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使⽤内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤⼩的调整。
realloc函数的使用方式为:void* realloc (void* ptr, size_t size)
void*:这个表示这个可以是任何的类型,比如int* ,char*,这个类型是由使用者自己定义的。
void* ptr:表示然后类型的指针地址都可以。
size_t size:调整之后新⼤⼩。
使用realloc函数是要注意以下几点
(1) 返回值为调整之后的内存起始位置。
(2)这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到 新 的空间。
(3)realloc在调整内存空间的是存在两种特殊情况
情况一:原有空间之后有⾜够⼤的空间
当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。
通过调试的时候可以明确的看到原有空间之后有⾜够⼤的空间要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化(两个变量指向同一地址)。
情况二:原有空间之后没有⾜够⼤的空间
当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。
通过调试的时候可以明确的看到将需要调整的地址调整超过原有空间的内存之后,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤。这样函数返回的是⼀个新的内存地址(两个变量明显不是指向同一地址)。
举个例⼦:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(5 * sizeof(int)); //开辟5个为整形的动态内存
if (p == NULL) //判断是否开辟成功
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 5; i++) //对p[i]进行赋值
{
*(p + i) = i + 1; //*(p+i)==p[i]
}
int* ptr = (int*)realloc(p, 40); //将5个整形变量动态内存调整为10个
if (ptr != NULL) //判断是否开辟成功
{
p = ptr;
int i = 0;
for (i = 5; i < 10; i++)
{
*(p + i) = i + 1; //*(p+i)==p[i]
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
}
else
{
perror("realloc");
free(p);
p = NULL;
}
return 0;
}
通过调试可以明确的看到我们使用malloc成功开辟了5个整形变量的动态内存
对p[i]进行赋值
然后通过realloc函数将5的动态开辟的空间成功改为了10个
也通过相应的赋值
通过输出发现这个代码是没有问题的
注意
free函数进行释放动态内存的传入的时候必须是原始首地址,不能是偏移后的地址,通过上面的代码,可以看到无论是malloc函数开辟的动态内存还是realloc或者calloc,真正方式改变的数值只有i,p至始至终都从来没有发生改变(*(p+i)==p[i])。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)