【c语言进阶】动态内存管理知识大全(下)(二)

简介: 【c语言进阶】动态内存管理知识大全(下)(二)

二:C/C++程序的内存区域的划分


 通过以上的题目我们发现,错误基本都是内存泄漏,非法访问,出函数该栈区被释放出现的问题。所以大家一定要弄清楚哪些在堆区,那些在栈区,这些变量会在什么时候销毁等等问题。



栈区(Stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

堆区(heap):由程序员分配释放管理,一般由 malloc,new等内部存储函数使用, 如果没收回,程序结束时由操作系统收回。创建堆时,一般在堆的头部 用一个字节存放堆的大小;回收堆时,通过查看这个 字节的内容,可得知需要释放的多大的内存。

全局区或静态区:存放 全局变量 和 静态变量 ,程序结束时由系统释放,分为全局初始化区和全局未初始化区。

常量区:存放 常量 ,结束时由系统释放。

程序代码区(上面4个区统称数据区):存放运行 或准备运行的程序代码,由系统调度


三:柔性数组:


 在 C99 标准中,结构中的最后一个元素的大小允许是未知大小的数组,而我们就将这个未知大小的数组称为柔性数组,并将这个数组成员称为柔性数组成员:


typedef struct A
{
  int a;
  int arr1[0];
  //柔性数组成员
}A;
typedef struct B
{
  int b;
  int arr2[];
  //柔性数组成员
}B;


1.柔性数组的特点:


结构中的柔性数组成员前面必须至少存在一个其他成员。

柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的。

sizeof 返回的这种结构大小不包括柔性数组的内存。

包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

例如:


typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4


 柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的。其实家人们在看sizeof()结果的时候就可以知道为什么柔性数组前面必须有其他成员了,因为sizeof返回的结构体大小不包括柔性数组的内存,所以如果没有其他成员的话,该结构体所占空间大小为0.这是不可能的。


2.柔性数组的使用:


 既然柔性数组的大小是不确定的,我们如何使用他呢?有了之前的动态内存管理函数的学习,再来研究柔性数组的使用就十分简单了,我们直接来看示例:


typedef struct test
{
  int i;
  int arr[];
}test;
int main()
{
  test a;
  //定义test类型结构体a
  test* p = (test*)malloc(sizeof(test) + 40);
  //malloc函数的返回值为指针类型,故使用结构体指针test*
  //使用malloc函数动态分配空间:test类型结构体的大小(不包含柔性数组) + 40字节
  if (p == NULL)
  {
  perror("malloc");
  //判断动态内存空间是否开辟成功
  return 1;
  }
  //业务处理1:
  p->i = 10;
  int i = 0;
  //给柔性数组元素赋值:
  for (i = 0; i < p->i; i++)
  {
  p->arr[i] = i;
  }
  //打印柔性数组元素:
  for(i=0;i<p->i;i++)
  {
  printf("%d ", p->arr[i]);
  }
  printf("\n");
  test* pp = (test*)realloc(p, sizeof(test) + 80);
  //使用realloc函数将结构指针指向的结构a进行扩容
  if (pp == NULL)
  {
  perror("realloc");
  //判断动态内存空间是否扩容成功
  return 1;
  }
  //业务处理2:
  pp->i = 20;
  for (i = 0; i < pp->i; i++)
  {
  pp->arr[i] = i + 9;
  }
  for (i = 0; i < pp->i; i++)
  {
  printf("%d ", pp->arr[i]);
  }
  printf("\n");
  free(pp);
  pp = NULL;
  return 0;
}


在这个示例中,我们首先定义了 test 类型结构体 a,接着使用了 malloc 函数为结构体中的 i与柔性数组分配了动态存储空间;接着在判断非空(动态内存空间分配成功)后给结构体成员 i 与柔性数组 arr 内元素赋值,并进行了打印;再接下来,我们通过使用 realloc 函数将包含柔性数组 arr 的结构体 a 扩容,并在扩容后给结构体内各成员重新赋值并打印;最后释放使用完毕的动态内存空间并将指针置空:



3.柔性数组的优势:


 但是同时我们又发现,我们使用柔性数组的目的在于希望使结构成员的空间变为动态,可大可小,那么为什么我们不将其写成指针,由指针成员来指向其它的空间呢?如下:


typedef struct test
{
  int i;
  int* p;
}test;
int main()
{
  test* ptr = (test*)malloc(sizeof(test));
  if (ptr == NULL)
  {
  perror("malloc");
  return 1;
  }
  ptr->p = (int*)malloc(40);
  if (ptr->p == NULL)
  {
  free(ptr->p);
  p=NULL;
  free(ptr);
  ptr = NULL;
  return 1;
  }
  return 0;
}



 在这里我们发现,虽然两段代码效果一样,但是大家会发现,柔性数组的只用开辟一次空间,而第二个需要开辟两次!并且释放也是一样。

优点:


方便内存释放。

有利于提升访问速度。

有利于减少内存碎片。


总结:


 加上今天内容的学习,我们就可以将动态内存空间灵活的运用在我们的程序代码之中了。并且通过使用并管理动态内存空间,还可以提高我们代码及程序的灵活性和执行效率,有助于我们写出更加优秀的程序。

 更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

目录
相关文章
|
2天前
|
程序员 编译器 C语言
C语言----动态内存分配(malloc calloc relloc free)超全知识点
C语言----动态内存分配(malloc calloc relloc free)超全知识点
14 6
|
2天前
|
存储 程序员 编译器
C语言:动态内存管理
C语言:动态内存管理
11 1
|
2天前
|
存储 编译器 程序员
C语言:数据在内存中的存储
C语言:数据在内存中的存储
13 2
|
2天前
|
存储 编译器 C语言
C语言:字符函数 & 字符串函数 & 内存函数
C语言:字符函数 & 字符串函数 & 内存函数
15 2
|
2天前
|
存储 C语言 开发者
【C言专栏】C 语言实现动态内存分配
【4月更文挑战第30天】C语言中的动态内存分配允许程序运行时按需分配内存,提供处理未知数据量的灵活性。这涉及`malloc()`, `calloc()`, `realloc()`, 和 `free()`四个标准库函数。`malloc()`分配指定大小的内存,`calloc()`同时初始化为零,`realloc()`调整内存大小,而`free()`释放内存。开发者需谨慎处理内存泄漏和指针使用,确保程序的稳定性和性能。动态内存分配是C语言中的重要技能,但也需要良好的内存管理实践。
|
2天前
|
存储 C语言
C语言进阶---------作业复习
C语言进阶---------作业复习
|
2天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
2天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
2天前
|
存储 编译器 C语言
C语言进阶第十课 --------文件的操作-1
C语言进阶第十课 --------文件的操作
|
2天前
|
存储 程序员 C语言
C语言进阶第九课 --------动态内存管理-2
C语言进阶第九课 --------动态内存管理