第五十三天学习记录:C语言进阶:动态内存管理Ⅰ

news/2024/10/17 10:29:25/

问:
栈区堆区静态区的大小是固定的吗?如果栈区满了,会向后2者借位置吗?
ChatAI答:
栈区、堆区和静态区的大小通常是由操作系统或编译器预定义的,不是固定的。这些区域的大小通常受到多种因素的影响,如系统物理内存大小、进程虚拟地址空间的大小、编译器和操作系统的实现等等。

当栈区满了时,是不能向堆区或静态区借位置的。栈区和堆区是两个独立的内存区域,它们使用不同的指针来管理内存空间。当栈区满了,意味着当前栈帧中的内存空间已经全部使用完毕,如果需要分配更多的内存,需要在栈上创建一个新的栈帧来继续分配内存。但是,由于栈区的大小通常是有限的,当创建的栈帧数量过多或栈帧过大时,就会出现栈溢出的情况,导致程序崩溃。

另一方面,堆区和静态区可以被程序共享使用,甚至可以跨越多个栈帧和函数调用。堆区由malloc、realloc、calloc等函数动态分配,当内存不够时,它可以通过请求更多的虚拟内存来扩展堆区的大小。静态区的大小在编译时就已经确定,通常包括全局变量、静态变量、常量等,而且静态区的大小不会随着程序运行而改变。

总的来说,栈区、堆区和静态区的大小通常是由各种因素共同决定的,并不是固定的。程序在运行时会预先将这些区域划分好,而当其中的某个区域已经满了时,就需要通过不同的方式来处理。

动态函数的介绍

malloc和free
C语言提供了一个动态内存开辟的函数:

void* malloc (size_t size); 

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

#define _CRT_SECURE_NO_WARNINGS 1#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>int main()
{//向内存申请10个整型的空间int* p = (int*)malloc(10 * sizeof(int));//int* p = (int*)malloc(INT_MAX);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;
}

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

void free(void* ptr);

free函数用来释放动态开辟的内存。
。如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定位的。
。如果参数ptr是NULL指针,则函数什么事都不做。

calloc
C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。原型如下:
void* calloc(size_t num,size_t size);
。函数的功能为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
。与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0.

问:
有calloc函数了还要malloc函数做什么?
ChatAI答:
calloc函数与malloc函数都是用来动态分配内存的函数,二者的不同之处在于内存分配和初始化的方式不同。

  • malloc函数分配内存空间时,内存中的数据不会被初始化,需要手动初始化才能使用。如果不进行初始化操作,申请的内存空间中可能残留着之前的垃圾数据,这就会对程序产生潜在的安全隐患。
  • 而calloc函数分配内存空间时,不仅会申请指定大小的内存,还会将其初始化为0。

因此,如果程序需要申请一块内存,并且希望该内存全部清零,那么就应该使用calloc函数;如果程序需要申请一块内存,但不需要初始化其中的数据,则需要使用malloc函数。

注意,calloc函数与malloc函数的返回值是相同的,它们都返回指向所分配内存的首字节的指针。需要注意的是,在使用calloc函数分配内存时,分配的空间大小第一个参数n和第二个参数size是传入的字节数的形式,例如calloc(n, size)中所请求的内存大小为 n*size 个字节。

总之,malloc函数和calloc函数都有各自的使用场景,根据实际情况选择使用。在申请内存时,应该根据需要进行初始化或者清零操作,确保程序的正确性和安全性。

#define _CRT_SECURE_NO_WARNINGS 1#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>int main()
{int * p = (int*)calloc(10 , sizeof(int));if (p == NULL){//打印错误原因的一个方式printf("%s\n", strerror(errno));}else{//正常使用空间int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}}free(p);p = NULL;return 0;
}

问:
如果malloc后又realloc将动态内存空间设置小了,再free,那么之前被改小的部分是在realloc的时候就释放了,还是要直到free的时候才释放?

ChatAI答:
如果使用realloc函数将先前申请的动态内存空间设置为比原先更小的大小,那么被裁剪的那一部分内存空间会立即被释放,即在realloc函数调用之后就被释放了。程序无法再访问被裁剪的那一部分内存空间,但是其内存空间并没有马上被OS收回,而是被保留在堆上,等待以后可能的内存分配请求。

当使用free函数释放指针所指的内存块时,其行为就与动态分配内存的方式是无关的。而是由malloc,calloc,realloc之类的函数所使用的堆管理器,根据内部的算法来决定如何回收内存块。因此,即使调用realloc函数将先前申请的内存缩小,被裁剪的那一部分内存空间也可能不会立即被OS收回,而可能暂时被保留在堆上,等待可能的内存分配请求。

在实际使用中,为了避免内存泄漏,建议在不需要使用先前申请的动态内存时,立即使用free函数将其释放,而不是让其保留在堆上。因此,如果在realloc函数之后又立即使用free函数释放指针所指的内存块,则被裁剪的那一部分内存空间应该能被立即回收,而不必等到程序结束或操作系统重启等时机。

realloc
realloc函数的出现让动态内存管理更加灵活。
为了合理的内存,我们会一定程度对内存的大小做灵活的调整。realloc函数可以做到对动态开辟内存大小的调整。函数原型如下:

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

。ptr是要调整的内存地址
。size调整之后新大小
。返回值为调整之后的内存起始位置
。这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
。realloc在调整内存空间的是存在两种情况:
。情况1:原有空间之后有足够大的空间

问:realloc函数调整后返回的指针地址和先前malloc开辟返回的地址会不会发生改变?
ChatAI答:
realloc函数的作用是调整先前通过malloc或calloc函数动态分配的内存块的大小,并返回指向调整后内存块的新地址的指针。当内存块当前的大小不足以满足分配新内存时,realloc函数则会在其他地方重新申请新的内存块,并将先前内存块的数据复制到新的地址中,最后释放原先的内存块。这样做可以保证分配的新内存块的大小不会小于要求的大小,并且可以尽量减少内存的碎片。

如果调用realloc函数会导致内存大小实际上并没有发生变化,那么realloc函数就可能返回与先前内存块相同的地址。但是,调用realloc函数重新调整内存块大小时,返回的指针地址可能会发生改变。具体来说,如果realloc函数需要在其他地方重新申请内存块,则返回的指针地址将是新内存块的首地址;如果内存块可以直接调整大小,则返回的指针地址可能与先前的地址相同,但是不能保证这种情况始终发生。

因此,为了保险起见,在使用realloc函数时应该始终将其返回的指针赋值给一个新的指针变量,并且在使用完毕后,应该使用新指针变量来访问内存块,而不是依赖先前的指针变量。这样可以避免内存访问错误和其它潜在的问题,并提高程序的可靠性和稳定性。

#define _CRT_SECURE_NO_WARNINGS 1#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.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) = 3 * i;}for (i = 0; i < 5; i++){printf("%d ", *(p + i));}}printf("\n");//realloc使用的注意事项://1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p//2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域//开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟//的内存空间的地址//3.得用一个新的变量来接收realloc函数的返回值(一般情况没什么,但若realloc申请失败返回了NULL,//就会把原指针给改成NULL,这样原来的内存也找不到了。)int* p2 = (int*)realloc(p, 40);printf("%p %p\n", p, p2);//free(p);    !!!这是错误的多此一举//p = NULL;   realloc函数已经自动将p处理了int i = 0;for (i = 5; i < 10; i++){*(p2 + i) = 3 * i;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p2);p2 = NULL;return 0;
}

http://www.ppmy.cn/news/86696.html

相关文章

代码随想录补打卡 718 最长重复子数组 1143 最长公共子序列 1035 不相交的线

718 最长重复子数组 代码如下 func findLength(nums1 []int, nums2 []int) int { //三道题思路基本一致&#xff0c;dp[i][j]表示的是到第下标为i-1和j-1的两个数组重复元素的个数如果nums[i-1][j-1]相同&#xff0c;那么重复元素的个数要加1并且同时向后移动 dp : make([…

Three.js--》建模软件如何加载外部3D模型?

目录 三维建模软件的介绍 Blender官方文档介绍 Blender软件安装 GLTF格式简介 gltf不同文件形式 看过我之前讲解的three文章的人都知道&#xff0c;我在创建模型的时候都没有使用three.js自带的一些简单模型&#xff0c;而是引入外部的模型并加载到页面上&#xff0c;简言…

JavaSE_day37(异常介绍,异常处理方式2种,确定相对路径的技巧两种,如何处理多个异常,Throwable的常用方法,Fianlly关键字)

1 A.java /*异常&#xff08;机制&#xff09;介绍 * Exception in thread "main" java.lang.ArithmeticException: / by zero at com.itjinling_01.ExceptionDemo.main(ExceptionDemo.java:5) 我们在写代码的时候&#xff0c;经常的出现一些小问题&…

【云原生|Kubernetes】05-Pod的存储卷(Volume)

【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume) 文章目录 【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume)简介Volume类型解析emptyDirHostPathgcePersistentDiskNFSiscsiglusterfsceph其他volume 简介 Volume 是Pod 中能够被多个容器访问的共享目录。 Kubern…

MAAS搭建

要求 https://maas.io/docs/installation-requirements 安装maas sudo snap install --channel=3.3 maas安装postgres sudo apt update -y sudo apt install -y postgresql设置数据库 sudo -i -u postgres psql -c "CREATE USER \"$MAAS_DBUSER\" WITH E…

2021-2023外包生涯的遗憾屈辱过错总结

遗憾 最大的遗憾是&#xff1a; 做的项目并发量、用户量不大。背锅&#xff0c;被人甩锅的时候&#xff0c;没有进行明确的反击。 屈辱 项目&#xff1a;2021-2022 打印日志的时候&#xff0c;如果实体重写了toString()&#xff0c;即使使用Map类型&#xff0c;也不需要使…

DJ5-4 链路层交换机

目录 一、链路层交换机 二、交换机&#xff1a;支持多节点同时传输 三、交换机&#xff1a;转发表或称交换表 四、交互机&#xff1a;自学习 1. 单个交换机自学习/转发的例子 2. 多个交换机自学习/转发的例子 五、交换机&#xff1a;数据帧的过滤/转发 六、交换机的交换…

java矢量切片实现

概述 可通过多种方式实现矢量切片的制作&#xff0c;前面讲到了基于postgis数据库、tippecanoe、Qgis等方式&#xff0c;本文讲述基于spring Boot框架下java的实现。 实现效果 实现代码 后端代码 引入依赖 <dependency><artifactId>giscat-vector-mvt</art…