动态内存管理2+柔性数组

一、动态内存经典笔试题分析

分析错误并改正

题目1

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

错误的原因:

1.str传给p的时候(值传递),p是str的临时拷贝,有自己独立的空间,当GetMemory函数内部申请了空间后,地址放在p中时,str依然是NULL。当GetMemory函数返回之后,strcpy拷贝的时候,形成了非法访问内存
2.在GetMemory函数内部,动态申请了内存,但是没有释放,会内存泄露
3.添加对 malloc 返回值的检查,避免分配失败时操作空指针。

改正(采用传址调用):

void GetMemory(char** p)
{*p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(&str);// 传递指针的地址if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

或者返回动态分配的指针:

char* GetMemory()
{char* p = (char *)malloc(100);return p;//return (char*)malloc(100);
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

题目2

返回栈空间地址的问题

char *GetMemory(void)
{char p[] = "hello world";// 局部数组,存储在栈内存return p;// 函数结束后,栈内存被释放,p的地址失效
}
void Test(void)
{char *str = NULL;str = GetMemory();//函数返回后,局部变量 p 的内存被系统回收,但 str 仍指向该地址,形成悬挂指针。printf(str);// 可能输出乱码或崩溃
}
int main()
{Test();return 0;
}

错误的原因:

GetMemory 函数内部定义了一个局部数组 char p[] = "hello world";,并返回其指针。
问题:局部数组存储在栈内存中,函数结束后栈内存被释放,此时返回的指针指向无效内存(悬挂指针)。后续通过 str 访问会导致未定义行为(如输出乱码或程序崩溃)。
悬空指针是指向已经被释放或无效内存区域的指针。这种指针虽然保留了之前的内存地址,但地址中的内容可能已被系统回收或重新分配,访问它会导致 未定义行为(如程序崩溃、数据错误、输出乱码等)。

改正:
1.返回字符串常量的指针:

char *GetMemory(void)
{char* p = "hello world";return p;// 字符串常量存储在只读区,生命周期为整个程序//return "hello world";
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}

2.使用动态内存分配:

char *GetMemory(void)
{char* p = (char*)malloc(strlen("hello world")+1);// +1 用于容纳 '\0'if(p != NULL){strcpy(p,"hello world");}return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){printf("%s\n",str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

3.使用静态变量:

char *GetMemory(void)
{static char p[] = "hello world";// 静态变量生命周期为整个程序return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}

题目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);
}
int main()
{Test();return 0;
}

错误的原因:malloc的返回值没有判断并且存在内存泄露(没有free)

改正:

void GetMemory(char **p, int num)
{*p = (char *)malloc(num);
}
void Test(void)
{char *str = NULL;GetMemory(&str, 100);if(str != NULL){strcpy(str, "hello");printf("%s\n",str);}free(str);str = NULL:
}
int main()
{Test();return 0;
}

题目4

void Test(void)
{char *str = (char *) malloc(100);//未对malloc返回值进行判断strcpy(str, "hello");free(str);if(str != NULL)// str 未被置空,条件仍为真{strcpy(str, "world");// 操作已释放的内存(未定义行为)printf(str);// 可能崩溃或输出乱码}
}
int main()
{Test();return 0;
}

错误的原因:未对malloc返回值进行判断,str 未被置空,是一个悬空指针

改正:

void Test(void)
{char *str = (char *) malloc(100);if(str != NULL){strcpy(str, "hello");printf("%s\n", str);}free(str);str = NNULL;// 置空指针,避免误用// 后续操作需确保指针有效if(str != NULL){// 此处代码永远不会执行strcpy(str, "world");printf(str);}
}
int main()
{Test();return 0;
}

二、柔性数组

C99中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员

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

有些编译器会报错⽆法编译可以改成:

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

2.1 柔性数组的特点

1.结构中的柔性数组成员前⾯必须至少⼀个其他成员。

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

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

struct st_type
{//1.结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。int i;int arr[];//柔性数组成员
}type_a;
int main()
{//2.sizeof返回的这种结构⼤⼩不包括柔性数组的内存。printf("%d\n", sizeof(type_a));//输出的是4return 0;
}
//3.包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。
struct S
{int i;char arr[];
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S) + 10*sizeof(char));if(ps == NULL){return 1;}ps->i = 100;printf("%d\n", ps->i);int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}for(i = 0;i < 10; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q}printf("\n");//增容struct S* ptr = (struct S*)realloc(ps,sizeof(struct S) + 20*sizeof(char));if(ptr != NULL){ps = ptr;}else{perror("realloc");return 1;}for(i = 10;i < 20; i++){ps->arr[i] = 'A';}for(i = 0;i < 20; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q A A A A A A A A A A}free(ps);ps = NULL;return 0;
}

柔性数组成员⽤malloc()函数进⾏内存的动态分配后获得空间,对结构体进行计算大小,还是不会算进去,只算除了柔性数组外的大小

printf("%d\n", sizeof(struct S));//4

2.2 柔性数组的优势

1.方便内存释放

2.这样有利于访问速度

代码一:

struct S
{int i;char arr[];
};
int main()
{int i = 0;struct S* p = (struct S*)malloc(sizeof(struct S) + 100 * sizeof(char));//业务处理 p->i = 100;for (i = 0; i < 100; i++){p->arr[i] = i;}free(p);p = NULL;return 0;
}

1.malloc一次      2.free一次      3.空间连续

代码二:

struct S
{int i;char* arr;
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S));if(ps == NULL){perror("malloc");return 1;} ps->i = 100;ps->arr = (char*)malloc(sizeof(char) * 10);if(ps->arr == NULL){perror("malloc->arr");return 1;}//使用int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}//释放free(ps->arr);ps->arr = NULL;free(ps);ps = NULL;return 0;
}

1.malloc两次      2.free两次      3.内存空间不连续(内存碎片多,浪费空间)

三、内存区域划分

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

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

2. 堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅ 式类似于链表。

3. 数据段(静态区):(static)存放全局变量、静态数据。程序结束后由系统释放。

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

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

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

相关文章

AI写PPT可以用吗?我测试了3款AI写PPT工具,分享感受

上周五临下班&#xff0c;领导突然让我周末赶出一份季度营销报告 PPT&#xff0c;还要求周一晨会展示。看着空荡荡的 PPT 页面&#xff0c;我满心都是绝望 —— 周末不仅泡汤&#xff0c;搞不好还得熬夜到凌晨。好在同部门的前辈给我推荐了几款 AI 写 PPT 工具&#xff0c;没想…

PrimeVul论文解读-如何构建高质量漏洞标签与数据集

目录 1. 引入2. 现有漏洞识别方案的不足2.1 数据集中label不准2.2 数据重复2.3 测评标准不够好 3. 现有漏洞识别数据集分析3.1 关于现有数据集中label的准确率分析3.2 关于现有数据集中数据泄露&#xff08; Data Leakage&#xff09;情况分析 4. 漏洞识别测评5. PrimeVul数据集…

关于数据湖和数据仓的一些概念

一、前言 随着各行业数字化发展的深化,数据资产和数据价值已越来越被深入企业重要发展的战略重心,海量数据已成为多数企业生产实际面临的重要问题,无论存储容量还是成本,可靠性都成为考验企业数据治理的考验。本文来看下海量数据存储的数据湖和数据仓,数据仓库和数据湖,…

linux-----------------库制作与原理(下)

1.ELF文件 要理解编译链链接的细节&#xff0c;我们不得不了解⼀下ELF⽂件。其实有以下四种⽂件其实都是ELF⽂件&#xff1a; • 可重定位⽂件&#xff08;Relocatable File &#xff09; &#xff1a;即 xxx.o ⽂件。包含适合于与其他⽬标⽂件链接来创 建可执⾏⽂件或者共享…

python-爬虫基础

爬虫本质&#xff1a;通过编写程序来获取到互联网上的资源。 我们的程序本质上就是模拟浏览器 一个简单的小爬虫&#xff1a; 只需要三步&#xff1a; from urllib.request import urlopen #url是网址&#xff0c;request意思是请求 这里跑出来的中文是这样的注意看&#…

单元化架构

目录 ​​​​​​​​编辑 单元化 逻辑单元 单元化 多地多机房部署&#xff0c;是互联网系统的必然发展方向&#xff0c;一个系统要走到这一步&#xff0c;也就必然要解决上面提到的问题&#xff1a;流量调配、数据拆分、延时等。业界有很多技术方案可以用来解决这些问题&…

【免杀】C2免杀技术(五)动态API

一、什么是动态API 在C2免杀领域中&#xff0c;“动态API” 主要指的是绕过静态检测的一种技术手段&#xff0c;其本质是运行时动态解析和调用Windows API函数&#xff0c;而不是在程序编译阶段就明确引用这些API。这种方式可以有效躲避静态分析工具和杀软的签名识别。 为什么…

Python爬虫实战:研究JavaScript压缩方法实现逆向解密

一、引言 在数字化信息爆炸的时代,网络数据已成为驱动各行业发展的核心资产。Python 凭借其丰富的库生态和简洁的语法,成为网络爬虫开发的首选语言。然而,随着互联网安全防护机制的不断升级,网站普遍采用 JavaScript 压缩与混淆技术保护其核心逻辑和数据传输,这使得传统爬…

HTTP 请求走私(HTTP Request Smuggling)

HTTP 请求走私&#xff08;HTTP Request Smuggling&#xff09;是一种通过利用前端代理&#xff08;如负载均衡器、CDN&#xff09;和后端服务器在 解析 HTTP 请求时存在不一致性 的漏洞&#xff0c;从而实现 注入恶意请求 的攻击技术。 一、基本原理 HTTP 请求走私主要依赖两…

【Google机器学习实践指南(线性回归篇)

&#x1f50d; Google机器学习实践指南&#xff08;线性回归篇&#xff09; Google机器学习实战(3)-单变量线性回归核心解析&#xff0c;掌握房价预测模型 一、建模流程全景图 ▲ 四大核心步骤&#xff1a; 数据可视化→特征工程→模型训练→预测推理 二、房价预测实战 1. …

python打卡day16

NumPy 数组基础 因为前天说了shap&#xff0c;这里涉及到数据形状尺寸问题&#xff0c;所以需要在这一节说清楚&#xff0c;后续的神经网络我们将要和他天天打交道。 知识点&#xff1a; numpy数组的创建&#xff1a;简单创建、随机创建、遍历、运算numpy数组的索引&#xff1a…

ubuntu 20.04 更改国内镜像源-阿里源 确保可用

镜像源是跟linux版本一一对应的,查询自己系统的版本号&#xff1a; 命令&#xff1a;lsb_release -a macw:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal macw:~$…

基于OpenCV的SIFT特征和FLANN匹配器的指纹认证

文章目录 引言一、概述二、代码解析1. 图像显示函数2. 核心认证函数2.1 创建SIFT特征提取器2.2 检测关键点和计算描述符&#xff08;源图像&#xff09;2.3 检测关键点和计算描述符&#xff08;模板图像&#xff09;2.4 创建FLANN匹配器2.5 使用K近邻匹配 3. 匹配点筛选4. 认证…

四品种交易策略

策略概述 策略思路: 交易品种:同时交易四个品种,每个品种使用总资金的10%。 合约选择:使用连续合约(data0)发出交易信号,实际交易 主力合约(data1)和下一个主力合约(data2)。 资金管理:总资金用A_CurrentEquity表示,交易手数据此计算。 止损执行:盘中达到止损…

MySQL事务的一些奇奇怪怪知识

Gorm事务有error却不返回会发生什么 Gorm包是大家比较高频使用。正常的用法是&#xff0c;如果有失败返回error&#xff0c;整体rollback&#xff0c;如果不返回error则commit。下面是Transaction的源码&#xff1a; // Transaction start a transaction as a block, return …

时序数据库、实时数据库与实时数仓:如何为实时数据场景选择最佳解决方案?

随着物联网、金融交易、在线游戏等场景对实时数据处理需求的增长&#xff0c;市场上涌现出多种专门针对实时数据处理的数据库解决方案。然而&#xff0c;面对时序数据库、实时数据库和实时数据仓库这三种看似相似的技术&#xff0c;许多技术决策者常常感到困惑&#xff1a;它们…

Spring3+Vue3项目中的知识点——JWT

全称&#xff1a;JOSN Web Token 定义了一种简洁的、自包含的格式&#xff0c;用于通信双方以json数据格式的安全传输信息 组成&#xff1a; 第一部分&#xff1a;Header&#xff08;头&#xff09;&#xff0c;记录令牌类型、签名算法等。 第二部分&#xff1a;Payload&am…

微服务架构详解

微服务架构详解:从概念到实践(附代码案例) 目录 微服务架构详解:从概念到实践(附代码案例) 一、微服务架构概述 1.1 什么是微服务? 1.2 微服务的核心思想 二、微服务架构的优势与挑战 2.1 优势 2.2 挑战 三、微服务架构的核心组件 3.1 服务注册与发现 示例代…

linux下编写shell脚本一键编译源码

0 前言 进行linux应用层编程时&#xff0c;经常会使用重复的命令对源码进行编译&#xff0c;然后把编译生成的可执行文件拷贝到工作目录&#xff0c;操作非常繁琐且容易出错。本文编写一个简单的shell脚本一键编译源码。 1 linux下编写shell脚本一键编译源码 shell脚本如下&…

学习!FastAPI

目录 FastAPI简介快速开始安装FastApiFastAPI CLI自动化文档 Reqeust路径参数Enum 类用于路径参数路径参数和数值校验 查询参数查询参数和字符串校验 请求体多个请求体参数嵌入单个请求体参数 CookieHeader表单文件直接使用请求 ResponseResponse Model多个关联模型 响应状态码…