【C语言进阶】之动态内存管理笔试题及柔性数组

【C语言进阶】之动态内存管理笔试题

  • 1.动态内存管理笔试题汇总
    • 1.1第一道题
    • 1.2第二道题
    • 1.3第三道题
    • 1.4第四道题
  • 2.C/C++内存管理
  • 3.柔性数组
    • 3.1什么是柔性数组
    • 3.2柔性数组的使用
    • 3.2柔性数组的优点

📃博客主页: 小镇敲码人
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞
前言:接上一篇博客【C语言进阶】之动态内存管理,今天来跟着博主把理论应用在实践之中,彻底掌握动态内存管理的相关知识!!!!

1.动态内存管理笔试题汇总

1.1第一道题

char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}

请问运行Test函数会有怎样的结果呢?
运行结果:

在这里插入图片描述

解析:数组p是函数里开的一个临时变量,它的空间开在栈区上,出了GetMemory函数作用域它的生命周期结束,系统就把它的空间回收了,所以你返回的地址是系统已经回收的地址,里面的内容是未知的,是一串字符。

另外printf打印字符串,可以直接传字符串首元素的地址打印。

1.2第二道题

void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}

运行结果:

在这里插入图片描述
解析:程序直接崩溃,这是为什么呢?明明这次已经在堆区开辟空间了呀,实际上,我们并没有改变str,如果你不相信,我们可以来验证一下:

在这里插入图片描述
在调用函数GetMemory后为什么str的值没有改变呢?因为str和p都是一级指针,它们两个唯一的相似之处就是它们的值是一样的,本质还是值传递,想改变一级指针的值,需要传它的地址,我们把代码这样改就对了:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p)
{*p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{Test();return 0;
}

在这里插入图片描述

1.3第三道题

void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}

请问运行Test 函数会有什么样的结果?

运行结果:
在这里插入图片描述
这道题和上一道题我们修改后的类似不做过多阐述,但是有个问题,虽然编译器没有报错,但是它动态开辟的空间没有释放,会造成内存泄露。

1.4第四道题

void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}

请问运行Test 函数会有什么样的结果?
运行结果:

在这里插入图片描述
解析:
虽然运行结果没有问题,但其实这段代码是不对的,原因就是我们在free动态开辟的空间后没有及时的去将str置为空指针,使他成为了一个野指针,那片地址已经不属于我们了,你还进行写入和访问,显然是非法的,所以我们应该养成好习惯,free后就要将指针变量置空。

2.C/C++内存管理

在这里插入图片描述

C/C++内存分配的几个区域:

1.栈区(stack):栈区主要放一些函数中创建的局部变量,生命周期在函数结束时就结束了,内存也被系统(OS)回收,这个区域通常存放临时变量、函数的参数、函数的返回数据、返回地址等。读写效率高,但是容量有限。
2.堆区(heap):这个区域主要是动态开辟的内存存放的地方,通常只有程序结束时系统才会回收其内存,除非你手动使用去回收,像C语言中使用的free函数。
3.数据段(静态区 static):这个内存区域主要存放静态变量、全局变量,生命周期也是全局,只有程序运行结束系统才会回收其空间。
4.代码段:这个区域内存主要存放函数体(类成员函数和全局函数)的二级制代码。

有了上面那幅图,相信你对动态内存管理又有了更深的理解,也可以更好的理解static这个关键字了:
1.一般的临时变量开在栈区,栈区的特点是,出了作用域,里面的临时变量就会被销毁。
2.static修饰的静态变量在数据段,生命周期变长了,在程序运行结束的时候它的空间才会被系统回收。

3.柔性数组

3.1什么是柔性数组

C99标准中,如果结构体的最后一个数组它的大小是未知的,我们就把那个数组叫做柔性数组。

typedef struct ss
{int a;int b[0];
}flexarr;

如果上面那种你的编译器不能通过,可以尝试写成下面这样:

typedef  struct sss
{int a;int b[];
}flexarr;

关于柔性数组有几点需要说明的:
1.sizeof计算结构体的大小时,是不将柔性数组的大小计算在内的。
2.当你要给柔性数组用malloc()函数开空间时,大小应该要比结构体的大小要大,以便于系统给柔性数组分配空间。
3.柔性数组前面必须要有至少要有一个成员。
如果你不相信我们可以用下面代码来验证一下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct ss
{int a;int b[0];
}flexarr;int main()
{printf("%d", sizeof(flexarr));return 0;
}

运行结果:
在这里插入图片描述

3.2柔性数组的使用

通过下面代码我们来演示柔性数组的使用:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct ss
{int a;int b[0];
}flexarr;int main()
{flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100 * sizeof(int));if (p == NULL){perror("malloc failed");exit(-1);}p->a = 100;memset(p->b, 0, sizeof(int) * 100);for (int i = 0; i < 100; i++){p->b[i] += 1;}printf("%d\n", p->a);for (int i = 0; i < 100; i++){printf("%d ", p->b[i]);}free(p);return 0;
}

运行结果:

在这里插入图片描述

flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100 * sizeof(int));

这个代码相当于给柔性数组开了100个int的空间。

3.2柔性数组的优点

上述flexarr也可以这样设计:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct ss
{int a;int* b;
}flexarr;int main()
{flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100);if (p == NULL){perror("malloc failed");exit(-1);}p->a = 100;p->b = (int*)malloc(sizeof(int) * 100);memset(p->b, 0, sizeof(int) * 100);for (int i = 0; i < 100; i++){p->b[i] += 1;}printf("%d\n", p->a);for (int i = 0; i < 100; i++){printf("%d ", p->b[i]);}free(p->b);p->b = NULL;free(p);p = NULL;return 0;
}

运行结果:

在这里插入图片描述

第一种方法和第二种相似,但是柔性数组有一定的优势:
1.方便内存释放

  • 如果我们的代码是在一个函数里面,给用户使用,用户是看不到我们具体的实现,给用户返回一个结构体,你在里面进行二次内存分配,用户只知道释放结构体的大小,怎么能知道还需要释放里面的动态数组呢?这样就会造成内存泄漏,不安全。
  • 2.提高了访问速度
    代码1使用柔性数组,只进行了一次内存分配,是连续的,可以提高访问速度,减少了内存碎片。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/160493.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【已解决】设置SSH主机:VS Code-正在本地下载 VS Code 服务器

问题描述 很简单&#xff0c;就是我电脑强制重启之后用vscode再去连服务器&#xff0c;发现连不上了 解决办法 如上图&#xff0c;点击重试按钮&#xff0c;下面的这些东西就可以复制粘贴了 ctrf查找commit&#xff0c;这个时候就能找到一串d037ac076cee195194f93ce6fe2bdfe296…

Spring Security OAuth 2.0 资源服务器— JWT

目录 一、JWT的最小依赖 二、JWT的最基本配置 1、指定授权服务器 2、初始预期&#xff08;Startup Expectations&#xff09; 3、运行时预期&#xff08;Runtime Expectations&#xff09; 三、JWT认证是如何工作的 四、直接指定授权服务器 JWK Set Uri 五、提供 audie…

中国联通携手华为助力长城精工启动商用5G-A柔性产线

[中国&#xff0c;河北&#xff0c;2023年11月3日] 近日&#xff0c;中国联通携手华为助力精诚工科汽车系统有限公司保定自动化技术分公司&#xff08;简称长城精工自动化&#xff09;启动5G-A超高可靠性超低时延柔性产线的商用阶段。 在河北保定精工自动化工厂&#xff0c;5G…

Java医院HIS系统源码

Java医院HIS系统源码 项目描述 该项目是用springbootlayuishiro写的医院管理系统&#xff0c;该系统的业务比较复杂&#xff0c;数据库一共有36张表。项目的视频业务参考文档&#xff0c;都在百度云盘中。可以先看看视频和参考文档。 运行环境 jdk8mysqlIntelliJ IDEAmaven…

STM32中微秒延时的实现方式

STM32中微秒延时的实现方式 0.前言一、裸机实现方式二、FreeRTOS实现方式三、定时器实现&#xff08;通用&#xff09;4、总结 0.前言 最近在STM32驱动移植过程中需要用到微秒延时来实现一些外设的时序&#xff0c;由于网上找到的驱动方法良莠不齐&#xff0c;笔者在实现时序过…

论文阅读:One Embedder, Any Task: Instruction-Finetuned Text Embeddings

1. 优势 现存的emmbedding应用在新的task或者domain上时表现会有明显下降&#xff0c;甚至在相同task的不同domian上的效果也不行。这篇文章的重点就是提升embedding在不同任务和领域上的效果&#xff0c;特点是不需要用特定领域的数据进行finetune而是使用instuction finetun…

【数据结构】冒泡排序 (码源实现)

冒泡排序 前言一、冒泡排序运行图例二、算法实现基本思路三、算法实现步骤四、算法码源详解五、冒泡排序效率分析&#xff08;一&#xff09;时间复杂度——O&#xff08;N^2&#xff09;&#xff08;二&#xff09;空间复杂度——O&#xff08;1&#xff09;&#xff08;三&am…

数字IC后端实现 |TSMC 12nm 与TSMC 28nm Metal Stack的区别

下图为咱们社区IC后端训练营项目用到的Metal Stack。 芯片Tapeout Review CheckList 数字IC后端零基础入门Innovus学习教程 1P代表一层poly&#xff0c;10M代表有10层metal&#xff0c;M5x表示M2-M6为一倍最小线宽宽度的金属层&#xff0c;2y表示M7-M8为二倍最小线宽宽度的金…

layui form表单 调整 label 宽度

这个可以调整所有label .layui-form-label {width: 120px !important; } .layui-input-block {margin-left: 150px !important; }情况是这样的&#xff0c;表单里有多个输入框&#xff0c;只有个别label 是长的&#xff0c;我就想调整一下个别长的&#xff0c;其它不变 <di…

OpenCV官方教程中文版 —— 图像修复

OpenCV官方教程中文版 —— 图像修复 前言一、基础二、代码三、更多资源 前言 本节我们将要学习&#xff1a; • 使用修补技术去除老照片中小的噪音和划痕 • 使用 OpenCV 中与修补技术相关的函数 一、基础 在我们每个人的家中可能都会几张退化的老照片&#xff0c;有时候…

【LeetCode刷题-链表】--146.LRU缓存

146.LRU缓存 方法一&#xff1a;哈希表双向链表 使用一个哈希表和一个双向链表维护所有在缓存中的键值对 双向链表按照被使用的顺序存储了这些键值对&#xff0c;靠近头部的键值对是最近使用的&#xff0c;而靠近尾部的键值对是最久使用的哈希表即为普通的哈希映射&#xff0…

6大场景,玩转ChatGPT!

文章目录 一、故事叙述提问举例 二、产品描述提问举例 三、报告撰写提问举例 四、邮件和信件撰写提问举例 五、新间稿和公告撰写提问举例 六、学术论文和专业文章撰写提问举例 本文是在GPT3.5版本下演示的 我们知道AI技术不仅能够自动生成文章和内容&#xff0c;还可以根据我们…