一、动态内存函数的介绍

二、常见的动态内存错误

三、柔性数组


一、动态内存函数的介绍

malloc:可以开辟一个动态的内存空间,在堆区

free:释放动态空间的函数

void* malloc ( size_t size);

头函数:#include<stdlib.h>\#include<malloc.h>

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定
  • 如果参数size为0,malloc的行为标准是未定义的,取决于编译器

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:

void free ( void* ptr );

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
  • 如果参数ptr是NULL指针,则函数什么事都不做

malloc和free都声明在stdlib.h头文件中

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

int main()
{
//向内存申请10个整形空间
int* p = (int*)malloc(10*(int));
if(p == NULL)
{
//打印错误原因的一个方式
printf("%s\n",strerror(errno));
}
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;
}

calloc:开辟一个空间,并把数据置为0

void* calloc ( size_t num,size_t size);

  • num: 空间大小
  • size: 每个元素的字节大小
  • 与malloc函数的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0

realloc:调整动态内存开辟的大小

void* realloc ( void* memblock,size_t size);

  • memblock:所创建好的动态内存名
  • size:新的字节大小
  • 返回的是新开辟的地址

realloc使用的注意事项:

  1. 如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
  2. 如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址
  3. 得用一个新的变量来接受realloc函数的返回值
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

int main()
{
int* p = (int*)malloc(20);
if(p == NULL)
printf("%s\n",strerror(errno));
else
{
int i = 0;
for(i = 0;i < 5;i++)
*(p+i) = i;
}
//在使用完malloc开辟的20个字节空间后
//我们还需要40个字节的空间
//可以使用realloc来调整
int* ptr = realloc(p,40);
if(ptr != NULL)
{
p = ptr;
int i = 0;
for(i = 0;i < 10;i++)
printf("%d ",*(p+i));
}

//释放空间
free(p);
p = NULL;

return 0;
}

C语言心得8(动态内存分配)_calloc

realloc可以当作malloc使用:

int* p = realloc(NULL,40);//---->malloc(40);

二、常见的动态内存错误

  • 对空指针得解引用操作//所以一定要if判断
void test()
{
int* p =(int*)malloc(INT_MaX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
  • 对动态开辟空间的越界访问
  • 对非动态开辟内存使用free释放
  • 使用free释放一块动态开辟内存的一部分
void test()
{
int* p = (int*)malloc(40);//10个整形
if(p != NULL)
return 0;
int i = 0;
for(i = 0;i < 10;i++)
*p++ = i;//不要使用这种方式!!!
//p的位置已经发生改变,不再是当前的空间地址
//所以会发生 使用free释放一块动态开辟内存的一部分的错误
free(p);
p = NULL;

return 0;
}
  • 对同一块动态内存多次释放
  • 动态开辟的内存忘记释放(内存泄漏)

C语言心得8(动态内存分配)_calloc_02

三、柔性数组

C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员

C语言心得8(动态内存分配)_realloc_03

柔性数组的特点:

  • 结构中的柔性数组成员前面必须至少一个其他成员
  • sizeof返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

好处:

  1. 方便内存释放
  2. 有利于访问的速度---减少内部碎片
#include<stdio.h>
#include<stdlib.h>
/*
//柔性数组的定义
struct S
{
int n;
int arr[];//柔性数组
};

int main()
{
int i = 0;
struct S* p = (struct S*)malloc(sizeof(struct S)+10*sizeof(int));
p->n = 100;
for(i = 0;i < 10;i++)
p->arr[i] = i;
for(i = 0;i < 10;i++)
printf("%d ",p->arr[i]);
free(p);
p = NULL;

return 0;
}
*/
//两种方式都可以得到同样的效果

//可等价的赋值
//这种方式更浪费空间,并且速度没那么快
struct S
{
int n;
int* arr;
};

int main()
{
struct S* p = (struct S*)malloc(sizeof(struct S));//给S开辟一个空间,并用p指针指向该地址
p->n = 100;
p->arr = (int*)malloc(10*sizeof(int));
int i = 0;
for(i = 0;i < 10;i++)
p->arr[i] = i;
for(i = 0;i < 10;i++)
printf("%d ",p->arr[i]);
free(p->arr);
p->arr = NULL;

free(p);
p = NULL;

return 0;
}