c语言学习第三十二课---内存开辟位置与柔性数组

简介: c语言学习第三十二课---内存开辟位置与柔性数组

c/c++程序的内存开辟

1.栈区:在执行函数时,函数的内部的储存单元都可以在栈上创建,结束时自动被释放。栈区的分配预案算内置预处理器模块,效率很高,但是分配的内存容量有限。存放不下就会产生栈溢出的现象。

栈区主要存放运行函数时被分配的局部变量,函数参数,返回数据,返回地址等        

2.堆区:一般由程序员分配释放,若若程序员不释放,结束时可能被OS回收,分配方式类似链表。

3.数据段:也是静态区,存放全局变量,静态数据。程序结束时有系统释放。

4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。

526a10656b314fb2b0e5919d5d5f14da.png

realloc函数补充

505c39e138864436a3540ff132cab159.png

realloc也可以像malloc一样申请空间

若第一个参数给的不是所扩容的指针,而是空指针,如realloc(NULL,20),那就是如malloc一样申请20个字节的空间。

柔性数组

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

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

柔性数组特点1:

.结构体中柔性数组成员前面必须至少有一个其他成员

   两种写法

typedef struct st_type
{
  int i;
  int a[0];//柔性数组成员
};
//或者这样写
struct S
{
  int n;
  char c;
  int arr[];//大小未知
};

柔性数组特点2:

.sizof返回的这种结构大小不包过柔性数组的内存

struct S
{
  int n;
  char c;
  int arr[];//未知,不知道它该占多大空间
};
int main()
{
  printf("%d", sizeof(struct S));//结果为8
}

这里的空间大小不会包括柔性数组成员的大小,大小计算参考之前结构体内容。

柔性数组特点3:

.包含柔性数组成员的结构用malloc函数惊醒内存分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的大小。

在创建结构体变量时,这里创建结构体的方式发生了改变,用动态内存开辟它的空间。

struct S
{
  int n;
  char c;
  int arr[];
};
int main()
{
  //假设柔性数组里我们想放10个整型数据
  struct S*  ptr=(struct S*)malloc(sizeof(struct S)+10*sizeof(int));
  if (ptr== NULL)
  {
    printf("%s\n", strerror(errno));
    return 1;
  }
  //使用
  ptr->n = 100;
  ptr->c = "w";
  for (int i = 0; i < 10; i++)
  {
    ptr->arr[i] = i;
    printf("%d", ptr->arr[i]);
  }
  //调整数组大小  大小调整为20ge 整形
  struct S* tmp = (struct S*)realloc(ptr, sizeof(struct S) + sizeof(int) * 20);
  if (tmp == NULL)
  {
    printf("%s\n", strerror(errno));
    return 1;
  }
  //使用
  ptr->n = 100;
  ptr->c = "w";
  for (int i = 0; i < 20; i++)
  {
    ptr->arr[i] = i;
    printf("%d", ptr->arr[i]);
  }
  //释放
  free(ptr);
  ptr - NULL;
}

利用malloc函数开辟结构体空间大小。

这里的柔性数组成员类似 int arr[10]存放10个整形数据,但是因为该数组的空间是由malloc开辟的,因此我们可是使用realloc函数来对空间进行调整根据我们的需求

通过调整结构体大小动态调整数组大小,柔性可变,故为柔性数组。

通过指针实现柔性数组的动态特点。

这里开辟空间时,只能先开辟结构体的空间,再开辟结构体里指针的空间。

这里的两个例子作用都是存放一到十的10个整形数据,扩容后存放一到二十的20个整型数组。

struct S
{
  int n;
  char c;
  int* arr;
};
int main()
{
  struct S* ptr = (struct S*)malloc(sizeof(struct S));
  if (ptr == NULL)
  {
    printf("%s\n", strerror(errno));
    return 1;
  }
  int* tmp = (int*)malloc(sizeof(int) * 10);
  if (ptr == NULL)
  {
    printf("%s\n", strerror(errno));
    return 1;
  }
  else
  {
    ptr->arr = tmp;//把开辟出来的空间给结构体里的指针
  }
  //使用
  ptr->n = 100;
  ptr->c = "w";
  for (int i = 0; i < 10; i++)
  {
    ptr->arr[i] = i;
    printf("%d", ptr->arr[i]);
  }
  //调整
  int *pc=realloc(ptr->arr, sizeof(int) * 20);
  if (pc == NULL)
  {
    return 1;
  }
  else
  {
    ptr->arr = pc;
  }
  //在使用
  ptr->n = 100;
  ptr->c = "w";
  for (int i = 0; i < 20; i++)
  {
    ptr->arr[i] = i;
    printf("%d", ptr->arr[i]);
  }
  //释放
  free(ptr->arr);
  ptr->arr = NULL;
  free(ptr);
  ptr = NULL;
  return 0;
}

注意:这里再释放时,我们刚开始下开辟的是结构体的空间,后开辟的结构体中指针的空间(扩容也是对结构体里的指针),故释放先释放结构体中指针的空间,在释放结构体的空间。

两者的区别

方案一(柔性数组)

malloc一次,free一次  

容易维护空间,不易出错,且malloc次数少,内存碎片少,空间利用率相对较高。

方案二(指针表示)

malloc两次,free两次,维护难度加大,容易出错  

malooc越多,内存碎片会增多,内存的使用率较低。

其实这里的内存节约也没多少,主要是理解其中特点。


相关文章
|
1月前
|
存储 大数据 C语言
C语言 内存管理
本文详细介绍了内存管理和相关操作函数。首先讲解了进程与程序的区别及进程空间的概念,接着深入探讨了栈内存和堆内存的特点、大小及其管理方法。在堆内存部分,具体分析了 `malloc()`、`calloc()`、`realloc()` 和 `free()` 等函数的功能和用法。最后介绍了 `memcpy`、`memmove`、`memcmp`、`memchr` 和 `memset` 等内存操作函数,并提供了示例代码。通过这些内容,读者可以全面了解内存管理的基本原理和实践技巧。
|
1月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
2月前
|
存储 JavaScript 前端开发
学习JavaScript 内存机制
【8月更文挑战第23天】学习JavaScript 内存机制
27 3
|
2月前
|
存储 NoSQL 程序员
C语言中的内存布局
C语言中的内存布局
38 0
|
2月前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
|
C# C语言 C++
从头开始学习c语言
以前的时候学习C语言时候认为C语言不过是一个学习的工具,学习一些理论知识就达到目的了,谁会用这么傻的语言啊,连个界面也没有,不像vb一下子就做出一个窗体来,放上几个按钮就可以了 后来学习C++的时候,认为C++与C是一种完全不同的语言,两者基本没有什么兼容性,当时在学校里看书的时候,一直比较纳闷为会c++的书上会写C++/C语言教程,到了现在才明白,我去C++与C本来就是一体是一脉相承的
1045 0
|
23天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
26天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
26天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
1月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。

热门文章

最新文章

下一篇
无影云桌面