【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.rhkb.cn/news/180790.html

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

相关文章

Webpack搭建本地服务器

一、搭建webpack本地服务 1.为什么要搭建本地服务器&#xff1f; 目前我们开发的代码&#xff0c;为了运行需要有两个操作&#xff1a; 操作一&#xff1a;npm run build&#xff0c;编译相关的代码&#xff1b;操作二&#xff1a;通过live server或者直接通过浏览器&#x…

Draft-P802.11be-D3.2协议学习__$9-Frame-Format__$9.3.1.22-Trigger-frame-format

Draft-P802.11be-D3.2协议学习__$9-Frame-Format__$9.3.1.22-Trigger-frame-format 9.3.1.22.1 Genreal9.3.1.22.2 Common Info field9.3.1.22.3 Special User Info field9.3.1.22.4 HE variant User Info field9.3.1.22.5 EHT variant User Info field9.3.1.22.6 Basic Trigge…

4K Video Downloader Pro v4.28.0(视频下载器)

4K Video Downloader Pro是一款专业的视频下载软件&#xff0c;支持从YouTube、Vimeo、Facebook、Instagram、TikTok等主流视频网站下载高质量的4K、HD和普通视频。它的操作流程简单&#xff0c;只需复制视频链接并粘贴到软件中即可开始下载。此外&#xff0c;该软件还提供了多…

C# Winform串口助手

界面设置 修改控件name属性 了解SerialPort类 实现串口的初始化&#xff0c;开关 创建虚拟串口 namespace 串口助手 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//在设计页面已经预先…

在Linux上通过NTLM认证连接到AD服务器(未完结)

这篇文章目前还没有实现具体的功能&#xff0c;只实现了明文登录&#xff0c;因为我缺少一些数据&#xff0c;比如通过密码生成hash&#xff0c;以及通过challenge生成response&#xff0c;我不知道怎么实现&#xff0c;因此这篇文章也是一个交流的文章&#xff0c;希望大佬看见…

深入理解网络IO复用并发模型

本文主要介绍服务端对于网络并发模型以及Linux系统下常见的网络IO复用并发模型。文章内容一共分为两个部分。 第一部分主要介绍网络并发中的一些基本概念以及我们Linux下常见的原生IO复用系统调用&#xff08;epoll/select&#xff09;等。第二部分主要介绍并发场景下常见的网…

el-table树状表格末行合计

首先,由于我的表头是动态的,所以就稍微复杂一点 效果图 表头数据格式是这样的 表格的数据格式是这样的 然后用合并的方法,此处就需要递归去计算,根据props去匹配每一列的数据,然后加起来,关键代码 //合计处理getSummaries(param) {const { columns, data } param;const su…

树结构及其算法-二叉排序树

目录 树结构及其算法-二叉排序树 C代码 树结构及其算法-二叉排序树 事实上&#xff0c;二叉树是一种很好的排序应用模式&#xff0c;因为在建立二叉树的同时&#xff0c;数据已经经过初步的比较&#xff0c;并按照二叉树的建立规则来存放数据&#xff0c;规则如下&#xff1…

解决方案中word中分节符的使用

解决方案中必不可少的两个“符号”&#xff0c;分页符&#xff0c;分节符 有了分节符&#xff0c;可以为不同节设置不同的页眉页脚、分栏格式、纸张大小及方向、页边距、不同节间采用不同的页码序号&#xff0c;常用的功能主要是把word下一次的由原来的“竖版”&#xff0c;变…

深入剖析:正则表达式的奥秘

简介 正则表达式&#xff08;Regular Expressions&#xff09;是一种强大的文本处理工具&#xff0c;一种用于匹配文本模式的字符串。它由特定的字符和操作符组成&#xff0c;用于定义一个搜索模式。这些搜索模式可以用于文本搜索、替换、验证和提取数据等多种用途。 以下是一…

canal+es+kibana+springboot

1、环境准备 服务器&#xff1a;Centos7 Jdk版本&#xff1a;1.8 Mysql版本&#xff1a;5.7.44 Canal版本&#xff1a;1.17 Es版本&#xff1a;7.12.1 kibana版本&#xff1a;7.12.1 软件包下载地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1jRpCJP0-hr9aI…

【Python语言】序列(列表,元组,字符串)切片操作

目录 序列切片操作 1.1 对list进行切片&#xff0c;从1开始&#xff0c;到5结束&#xff0c;步长为1 [ 1 : 5 ] 1.2 对tuple进行切片&#xff0c;从头开始&#xff0c;到最后结束&#xff0c;步长为1 [ : ] 1.3 对str进行切片&#xff0c;从头开始&#xff0c;到最…

『精』Vue 组件如何模块化抽离Props

『精』Vue 组件如何模块化抽离Props 文章目录 『精』Vue 组件如何模块化抽离Props一、为什么要抽离Props二、选项式API方式抽离三、组合式API方式抽离3.1 TypeScript类型方式3.2 文件分离方式3.3 对文件分离方式优化 参考资料&#x1f498;推荐博文&#x1f357; 一、为什么要抽…

Cordova插件开发二:高精度定位之卫星数据解析

文章目录 1.最终效果预览2.坐标获取方法3.在公共类中封装获取坐标的通用方法4.插件js中封装startGeoLocation方法5.插件主界面封装的方法1.最终效果预览 2.坐标获取方法 let obj = Object.assign({}, this.mapConfig.mapLocationObj)obj.isKeepCallBack = falselet res = await…

探索无限可能:APITable免费开源多维表格与可视化数据库远程访问的魅力

APITable免费开源的多维表格与可视化数据库公网远程访问 文章目录 APITable免费开源的多维表格与可视化数据库公网远程访问前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c…

代码生成器

Easycode Entity ##导入宏定义 $!{define.vm}##保存文件&#xff08;宏定义&#xff09; #save("/entity", ".java")##包路径&#xff08;宏定义&#xff09; #setPackageSuffix("entity")##自动导入包&#xff08;全局变量&#xff09; $!{au…

半导体工厂将应用哪些制造创新技术?

半导体工厂是高科技产业的结晶&#xff0c;汇聚了世界上最新的技术。 在半导体的原料硅晶片上绘制设计图纸&#xff0c;不产生误差&#xff0c;准确切割并包装&#xff0c;然后用芯片生产出我们使用的电脑、智能手机、手表等各种电子产品。绝大多数半导体厂都采用一贯的工艺&a…

android display 杂谈(三)WMS

用来记录学习wms&#xff0c;后续会一点一点更新。。。。。。 代码&#xff1a;android14 WMS是在SystemServer进程中启动的 在SystemServer中的main方法中&#xff0c;调用run方法。 private void run() { // Initialize native services.初始化服务&#xff0c;加载andro…

【PyTorch实战演练】AlexNet网络模型构建并使用Cifar10数据集进行批量训练(附代码)

目录 0. 前言 1. Cifar10数据集 2. AlexNet网络模型 2.1 AlexNet的网络结构 2.2 激活函数ReLu 2.3 Dropout方法 2.4 数据增强 3. 使用GPU加速进行批量训练 4. 网络模型构建 5. 训练过程 6. 完整代码 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我…

《Pytorch新手入门》第二节-动手搭建神经网络

《Pytorch新手入门》第二节-动手搭建神经网络 一、神经网络介绍二、使用torch.nn搭建神经网络2.1 定义网络2.2 torch.autograd.Variable2.3 损失函数与反向传播2.4 优化器torch.optim 三、实战-实现图像分类(CIFAR-10数据集)3.1 CIFAR-10数据集加载与预处理3.2 定义网络结构3.3…