来源于《高质量C/C++编程》的几道经典面试题

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: .str传给p的时候,p是str的临时拷贝,有自己的独立空间,当GetMemory函数内部申请了空间后,地址放在p中,str仍然是NULL。当Getmemory函数返回之后,strcpy拷贝的时候,形成了非法访问。

本文介绍几个非常经典的笔试题,原题+详细解析,供参考


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


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


结果:


01dc9231d8644c769e84e725668e08dd.png


解析:


void Getmemory(char* p)//用p来接收str的数据,p是形参,形参是实参的一份临时拷贝
{
  p = (char*)malloc(100);//给p开辟一个100字节的空间。
  //还有这里开辟了空间后,并没有释放,造成内存泄漏。
}
void test(void)
{
  char* str = NULL;
  Getmemory(str);//将str这个指针传过去,相当于传值过去,并不是传址输入,对形参的改变并不会影响实参。
  strcpy(str, "hello world");//这里的str还是空指针,并没有空间给它拷贝,形成NULL非法访问,这里就出错了。
  printf(str);
}
int main()
{
  test();
  return 0;
}


1.str传给p的时候,p是str的临时拷贝,有自己的独立空间,当GetMemory函数内部申请了空间后,地址放在p中,str仍然是NULL。当Getmemory函数返回之后,strcpy拷贝的时候,形成了非法访问。


2.在Getmemory函数内部,动态申请空间,但是没有释放,造成内存泄漏


正确做法1:


void Getmemory(char** p)//str是一级指针,&str就是二级指针
{
  *p = (char*)malloc(100);//*p就相当于str,就是给str开辟一个100字节的空间
}
void test(void)
{
  char* str = NULL;
  Getmemory(&str);//这里进行传址输入,利用形参修改实参
  strcpy(str, "hello world");//这里的str就拥有了100字节的空间了
  printf(str);
  free(str);//释放空间
  str=NULL//手动置NULL
}
int main()
{
  test();
  return 0;
}


正确做法2:


char* Getmemory(char* p)//那Getmemory函数返回值就要改成char *类型接收指针
{
  p = (char*)malloc(100);
  return p;//将p返回
}
void test(void)
{
  char* str = NULL;
  str=Getmemory(str);//这里用指针str来接收Getmemory函数内部形参开辟的空间
  strcpy(str, "hello world");
  printf(str);
  free(str);//释放空间
  str=NULL//手动置NULL
}
int main()
{
  test();
  return 0;
}

9bda877b8b95419c9962d543f9e05202.png


题目2:返回栈空间地址问题–非法访问


char* Getmemory(void)
{
  char p[] = "hello world";
  return p;
}
void test(void)
{
  char* str = NULL;
  str = Getmemory();
  printf(str);
}
int main()
{
  test();
  return 0;
}


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


结果:


06a72b0e201c4a2a846153b2d06faccd.png


解析:


char* Getmemory(void)
{
  char p[] = "hello world";//创建一个数组p,里面存放着字符串
  return p;//返回数组名,也就是返回了数组首元素的地址
}
void test(void)
{
  char* str = NULL;
  str = Getmemory();//Getmemory函数的返回值用str来接收
  //数组p的首元素地址用str来接收
  //但要注意的是,p数组是在Getmemory内部创建,创建在栈区,出了函数,这个空间就要返回给操作系统,不再受指针p操控。
  //所以str虽然得到了数组的首元素地址,但这片空间已经不再属于数组空间了,所以形成了非法访问
  printf(str);//打印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, "xiao tao");
  printf(str);
}
int main()
{
  test();
  return 0;
}


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


90d27fbb96524b95ab3f028bb2e13676.png


解析:


这个题目跟第一题的修改后的题目差不多


void Getmemory(char** p, int num)
{
  *p = (char*)malloc(num);//开辟100个字节空间给*p  *p就是str
  //所以给str开辟了100字节的空间
}
void test(void)
{
  char* str = NULL;
  Getmemory(&str, 100);//传址输入,可以通过对形参修改而修改实参
  strcpy(str, "xiao tao");//对str指向的空间进行拷贝
  printf(str);//打印拷贝后的空间内容
  //最最重要的一点是,空间申请后,释放呢?这里没有释放所以最后会造成内存泄漏
  free(str);//释放内存
  str = NULL;
}
int main()
{
  test();
  return 0;
}


题目4:非法访问


void test(void)
{
  char* str = (char*)malloc(100);
  strcpy(str, "xiao tao");
  free(str);
  if (str != NULL)
  {
    strcpy(str, "666");
    printf(str);
  }
}
int main()
{
  test();
  return 0;
}


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


3bb82f1211cb4e2f87691eee5e911720.png


解析:


void test(void)
{
  char* str = (char*)malloc(100);//开辟100个字节的空间
  strcpy(str, "xiao tao");//给指向srt的空间进行拷贝
  free(str);//释放空间
  //free的功能是将原先指向这块空间的指针与这块空间之间的联系断开,这块空间不再首这个指针调控,回归到操作系统,供有需要的使用
  //而free释放空间后并不会将原先指向这块空间的指针置为空指针,还是指向原先的地方,所以下面的操作就进行下去了
  //所以这个指针又去访问那块空间让其被覆盖成666,这就造成非法访问了
  if (str != NULL)//如果str不为空指针进行下面操作
  {
    strcpy(str, "666");
    printf(str);//打印指向str那块空间的内容
  }
}
int main()
{
  test();
  return 0;
}


正确做法:在不改变原意的基础上修改:


void test(void)
{
  char* str = (char*)malloc(100);//开辟100个字节的空间
  strcpy(str, "xiao tao");//给指向srt的空间进行拷贝
  free(str);//释放空间
  str = NULL;//释放空间后手动置为NULL,让下面的操作无法进行
  if (str != NULL)//如果str不为空指针进行下面操作
  {
    strcpy(str, "666");
    printf(str);//打印指向str那块空间的内容
  }
}
int main()
{
  test();
  return 0;
}


相关文章
|
1月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
48 2
|
1月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
46 0
|
3月前
|
编译器 C++ 开发者
C++一分钟之-C++20新特性:模块化编程
【6月更文挑战第27天】C++20引入模块化编程,缓解`#include`带来的编译时间长和头文件管理难题。模块由接口(`.cppm`)和实现(`.cpp`)组成,使用`import`导入。常见问题包括兼容性、设计不当、暴露私有细节和编译器支持。避免这些问题需分阶段迁移、合理设计、明确接口和关注编译器更新。示例展示了模块定义和使用,提升代码组织和维护性。随着编译器支持加强,模块化将成为C++标准的关键特性。
167 3
|
3天前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
12 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
28天前
|
Rust 安全 C++
系统编程的未来之战:Rust能否撼动C++的王座?
【8月更文挑战第31天】Rust与C++:现代系统编程的新选择。C++长期主导系统编程,但内存安全问题频发。Rust以安全性为核心,通过所有权和生命周期概念避免内存泄漏和野指针等问题。Rust在编译时确保内存安全,简化并发编程,其生态系统虽不及C++成熟,但发展迅速,为现代系统编程提供了新选择。未来有望看到更多Rust驱动的系统级应用。
42 1
|
14天前
|
程序员 C++ 容器
C++编程基础:命名空间、输入输出与默认参数
命名空间、输入输出和函数默认参数是C++编程中的基础概念。合理地使用这些特性能够使代码更加清晰、模块化和易于管理。理解并掌握这些基础知识,对于每一个C++程序员来说都是非常重要的。通过上述介绍和示例,希望能够帮助你更好地理解和运用这些C++的基础特性。
31 0
|
2月前
|
人工智能 JavaScript 开发工具
C++中的AI编程助手添加
今天为大家推荐一款适配了 Viusal Studio(本文使用),VS Code(本文使用),JetBrains系列以及Vim等多种编译器环境的插件 Fitten Code,Fitten Code 是由非十大模型驱动的 AI 编程助手,它可以自动生成代码,提升开发效率,帮您调试 Bug,节省您的时间,另外还可以对话聊天,解决您编程碰到的问题。 Fitten Code免费且支持 80 多种语言:Python、C++、Javascript、Typescript、Java等。
79 8
|
1月前
|
存储 编译器 C++
打破C++的神秘面纱:一步步带你走进面向未来的编程世界!
【8月更文挑战第22天】C++是一门功能强大但学习曲线陡峭的语言,提供高性能与底层控制。本文通过实例介绍C++基础语法,包括程序结构、数据类型、控制结构和函数。从简单的“Hello, C++!”程序开始,逐步探索变量声明、数据类型、循环与条件判断,以及函数定义与调用。这些核心概念为理解和编写C++程序打下坚实基础,引导你进入C++编程的世界。
33 0
|
3月前
|
存储 C++
【C++航海王:追寻罗杰的编程之路】一篇文章带你了解二叉搜索树
【C++航海王:追寻罗杰的编程之路】一篇文章带你了解二叉搜索树
31 1
|
3月前
|
算法 安全 编译器
【C++航海王:追寻罗杰的编程之路】C++11(四)
【C++航海王:追寻罗杰的编程之路】C++11(四)
31 0