详解动态内存管理(二)

简介: 详解动态内存管理

4. 几个经典的笔试题


4.1 题目1:


void GetMemory(char *p)     
{
  p = (char *)malloc(100);   
}
void Test(void)
{
  char *str = NULL;
  GetMemory(str);   
  strcpy(str, "hello world");
  printf(str);   //注意,该行代码没有错误!解析会讲
}

输出:


42ffa855fc63489f9950c59314734ab5.png


解析:


void GetMemory(char *p)    //用形参 p 接收 str 
{
  p = (char *)malloc(100);    //将动态开辟的内存首地址传给指针变量 p ,并没有传给 str
}
void Test(void)
{
  char *str = NULL;
  GetMemory(str);      //函数调用结束后,str 还是空指针
  strcpy(str, "hello world");    //无法将该字符串复制到空指针内
  printf(str);
}


为什么可以这样写:


printf(str);


我们先来看看 printf 函数的原型:


int printf ( const char * format, ... );


将格式所指向的 C 字符串写入标准输出(标准输出)。如果 format 包含格式说明符(以 % 开头的子序列),则格式后面的其他参数将被格式化并插入到结果字符串中,以替换它们各自的说明符。


还记得我们之前接触的 printf 的写法有:


printf("hello world");


之所以能打印出来,是因为我们将字符串的首元素 ‘h’ 的地址传入了 printf 函数中,那么我们直接传字符串的首元素地址 str 也是一样的


4.2 题目2:


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

输出:


07786f668a9543a29bbd6e5cdb09251b.png


解析:


char *GetMemory(void)
{
  char p[] = "hello world";
  return p;   //指针p指向了字符‘h’,
}             
void Test(void)
{
  char *str = NULL;
  str = GetMemory();  //接收指针p的值,但当该函数调用结束后,局部变量 p 所指向的内容就被销毁了
  printf(str);        //所以打印出来就是乱码
}


4.3 题目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);
}


输出:


7fb7b258198c42079832d68aca5ffa83.png


咋一看,似乎没啥问题,虽然输出没问题,但是我们开辟的动态内存没有进行释放,导致了内存泄漏,这虽然不影响结果,但也是很严重对问题。


4.4 题目4:


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


输出:


870e91bf84494ab1b730a637fab0b494.png


输出没有问题,我们看下面的警告,因为本题没有判断 str 是否为空,所以在复制字符串的时候警告,当 free 完后,那块动态内存开辟的空间就不属于我了,str 就是野指针了,再进行访问就是非法访问。

我们可以对其进行修改一下:


void Test(void)
{
  char* str = (char*)malloc(100);
  if(str == NULL)     //判断是否为空指针
  return 1;
  strcpy(str, "hello");
  free(str);
  str = NULL;  //养成习惯,free 后就置空,防止野指针
  if (str != NULL)     //这样就不会再出错了
  {
  strcpy(str, "world");
  printf(str);
  }
}


最后


下期讲解 C/C++ 程序的内存开辟和柔性数组。


相关文章
|
数据安全/隐私保护
常用的表单校验规则——邮箱/QQ/身份证号码/微信/电话/数字字母/整数/文本/密码等
常用的表单校验规则——邮箱/QQ/身份证号码/微信/电话/数字字母/整数/文本/密码等
430 0
|
Android开发 索引
Android流布局实现筛选界面
Android流布局实现筛选界面
174 0
|
Shell C# 索引
(C#)Windows Shell 外壳编程系列8 - 同后缀名不同图标?
原文 (C#)Windows Shell 外壳编程系列8 - 同后缀名不同图标? (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)   接上一节:(C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单   关于注册 动态库必须注册才能使用。
1091 1
|
监控 JavaScript 数据安全/隐私保护
|
4天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
15天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1312 5
|
2天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。