动态内存管理

本文详细介绍了C语言中的动态内存管理函数,包括malloc, free, calloc和realloc的使用,以及动态内存错误的常见情况。通过示例代码解释了如何正确分配和释放内存,以及避免内存泄漏和越界访问等问题。同时,讨论了柔性数组的概念和使用注意事项。
摘要由CSDN通过智能技术生成

动态内存管理

栈区:局部变量,函数的形式参数

堆区:动态内存分配malloc/free,calloc,realloc

静态区:全局变量,静态变量

malloc

void* malloc(size_t size);
  • 如果开辟成功,则返回一个指向开辟好空间的指针

  • 如果开辟失败,返回一个NULL指针,因此malloc的返回值一定要做检查

  • 返回的类型是void*,所有malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定

  • 如果参数size为0,malloc的行为是标准是未定义的,取决于编译器

free

void free(void* ptr);
  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
  • 如果参数ptr是NULL指针,则函数什么事都不做。
#include<stdio.h>
#include<stdlib.h>
int main()
{
  //int arr[10];//40个字节
  //char arr2[40];//40个字节
  //申请空间
  int* p = malloc(40); // 如果申请太大,会失败
  int* ptr = p;
  if (p==NULL)
  {
    perror("malloc");
    return 1;
  }
  for(int i = 0;i<10;i++)
  {
    *(ptr+i) = i;
  }
  //释放空间
  //free(p);//err  参数为起始地址
  free(ptr);
  ptr = NULL; //用完后要变成空指针
  /*
  if (ptr != NULL)
  {
	*ptr = 100;		
  }
  */
  return 0;
}
//如果不释放动态申请的内存的时候
//程序结束,动态申请的内存由操作系统自动回收
//但是如果程序不结束,动态内存是不会自动回收的,就会形成内存泄漏的问题

calloc

void* calloc (size_t num, size_t size); 
  • 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
int main()
{
  int* p =(int*)malloc(40);  // *p 都是随机值
  int* p = (int*)calloc(10,sizeof(int));//calloc申请的空间会被初始化为0
  if (p == NULL)
  {
    perror("calloc");
    return 1;
  }
  for(int i = 0;i<10;i++)
  {
    printf("%d ",*(p+i));
  }
  free(p);
  p = NULL;
    
}

realloc

void* realloc(void* memblock,size_t size)
  • ptr 是要调整的内存地址
  • size调整之后新大小
  • 返回值为调整之后的内存起始位置
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
int main()
{
  int* p = (int*)malloc(40);
  
  if(p == NULL)
  {
	perror("malloc");
    return 1;
  }
  for (int i = 0;i<10;i++)
  {
    *(p+i) = i;
  }
  //空间不够,希望能放20个元素,考虑扩容
  //realloc(p,80); // 把p这个空间扩容成80个字节 
  //如果原来位置的后面空间不够用,重新开辟空间,并数据移植,把原空间释放掉
  //如果原来位置的后面空间够用,直接开辟,扩容
  //p = realooc(p,80); err ,万一扩容失败,返回空指针
  int * ptr = (int*) realloc(p,80);
  if (ptr !=NULL)
  {
	p = ptr;
  }
  //开辟成功开始使用
  free(p);
  p = NULL;
  return 0;
}

常见的动态内存错误

对NULL指针的解引用操作

int main()
{
  int* p = (int*)malloc(1000);
  int i = 0;
  for (i = 0;i<250;i++)
  {
	*(p+i) = i; //error直接这样会警告,防止NULL空间开辟失败
  }
}

对动态开辟空间的越界访问

对动态内存的边界要检查

int main()
{
  int* p = (int*)malloc(100);
  int i = 0;
  for (i = 0;i<=25;i++)
  {
	*(p+i) = i; //error 循环26次,多循环一次
  }
}

对非动态开辟内存使用free释放

int main()
{
  int a = 10;
  int* p = &a;
  //...
  
  free(p);//error a是局部变量不能够用free释放
  p = NULL;
  return 0;
}

使用free释放一块动态内存的一部分

int main()
{
  int* p = (int*)malloc(100);
  if(p == NULL)
  {
	return 1;
  }
  int i = 0;
  for(i = 0;i<10;i++)
  {
	*p == i;
    p++;
  }
  free(p);//error 会报错必须是起始位置
  p=NULL;
  return 0;
}

对同一块动态内存多次释放

int main()
{
  int* p = (int*)malloc(100);
  if(p == NULL)
  {
	return 1;
  }
  free(p);
  p = NULL;
  //...
  free(p); //err 重复释放
}

动态开辟内存忘记释放

void test()
{
  int* p = malloc(100);
  //使用
}//出了test,p失效,内存就一直占用
int main()
{
  test();
  //...
  while(1)
  {
    ;
  }
  return 0;
}

题目

void GetMemory(char *p)// p是str的临时拷贝,没有传地址
{
    p = (char *)malloc(100); //内存泄漏了
}
void Test(void)
{
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");//对NULL的解引用操作,程序崩溃
    printf(str);
}
int main()
{
  test();
  return 0;
}
//直接挂掉,没有结果

修改后

void GetMemory(char *p)
{
    p = (char *)malloc(100); 
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str);
    strcpy(str, "hello world");//对NULL的解引用操作,程序崩溃
    printf(str);
}
int main()
{
  test();
  return 0;
}
char *GetMemory(void)
{
    char p[] = "hello world"; // hello world 出函数被销毁了
    return p;
}
//返回栈空间地址的问题
void Test(void)
{
    char *str = NULL; 
    str = GetMemory();//str是野指针 
    printf(str);
}
int main()
{
  Test();
  return 0;
}
test()
{
  int a =10;
  return &a;
}
int main()
{
  int* p = test();
  printf("%d\n",*p);//10  栈帧被覆盖
  printf("%d\n",*p);//就会错误
  return 0;
}
//打印出来也是错的
void GetMemory(char **p, int num)
{
    *p = (char *)malloc(num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello"); // 可以打印,但没有free
    printf(str);
    //忘记释放
    //free(str);
    str = NULL;
}
int main()
{
  Test();
  return 0;
}
void Test(void)
{
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);  // 野指针  
    //str = NULL;
    if(str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
} 
int main()
{
  Test();
  return 0;
}

柔性数组

C99中,结构最后一个元素允许是位置大小的数组,这就叫做"柔性数组"成员

typedef struct st_type
{
  int i ;
  int a[0];//柔性数组成员
}type_a;
typedef struct st_type
{
  int i ;
  int a[];//柔性数组成员
}type_a;

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

柔性数组的使用

struct s3
{
  int num;//4
  int arr[];//柔性数组成员
};
int main()
{
  struct s3* ps = (struct s3*)malloc(sizeof(struct s3)+40);
  if (ps == NULL)
  {
    perror("realloc\n");
    return 1;
  }
  // 4个字节给了num,40个给了柔性数组
  ps->num = 100;
  int i = 0;
  for(i = 0;i<10;i++)
  {
	ps->arr[i] = i; 
  }
  //扩容
  struct s3* ptr = (struct s3*)realloc(ps,sizeof(struct s3)+80);
  if (ptr == NULL)
  {
    perror("realloc\n");
    return 1;
  }
  else
  {
    ps = ptr;
  }
  // 使用
  free(ps);
  ps = NULL;
}
struct s4
{
  int num;
  int* arr;
}
int main()
{
  struct s4* ps = (struct s4*)malloc(sizeof(struct s4));
  if(ps == NULL)
  {
    return 1;
  }
  ps-> arr = (int*)malloc(40);
  if(ps->arr == NULL)
  {
    free(ps);
    ps = NULL;
    return 1;
  }
  //使用
  return 0;
}

柔性数组的优势

  • 方便内存释放
  • 有利于访问速度
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

love2study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值