当前位置: 首页 > news >正文

突破编程_C++_面试(基础知识(3))

面试题5:函数调用的过程

C++ 中函数的调用包含参数入栈、函数跳转、保护现场、回复现场等过程,重点过程如下:
(1)将函数的参数压入栈中,从右至左压入。
(2)调用函数时,将当前程序的执行位置(即返回地址)压入栈中。
(3)将函数的栈帧(也称为活动记录)压入栈中。栈帧包含了函数的局部变量、函数返回值、函数的上一级调用者的栈帧指针等信息。
(4)执行函数体内的语句,包括局部变量的声明和初始化、函数体语句的执行等。
(5)函数执行完毕后,将函数的返回值保存在寄存器中(或者栈中)。
(6)弹出函数的栈帧,并将返回值传递给上一级函数。
(7)将返回地址弹出栈中,程序跳转到该地址继续执行。
以如下代码为例( 64 位程序):

#include <iostream>int add(int a, int b)
{int sum = a + b;return sum;
}int main()
{int sum = add(1, 2);return 0;
}

首先给 main() 函数的第一行 int sum = add(1, 2); 打上断点,调试运行程序。
程序暂停后,查看当前汇编代码( VS2017 查看方法:右击当前代码页,选择转到反汇编):

int main()
{
00007FF67D8AA630  push        rbp  
00007FF67D8AA632  push        rdi  
00007FF67D8AA633  sub         rsp,108h  
00007FF67D8AA63A  lea         rbp,[rsp+20h]  
00007FF67D8AA63F  mov         rdi,rsp  
00007FF67D8AA642  mov         ecx,42h  
00007FF67D8AA647  mov         eax,0CCCCCCCCh  
00007FF67D8AA64C  rep stos    dword ptr [rdi]  
00007FF67D8AA64E  lea         rcx,[__81FC6F77_main2@cpp (07FF67D9E41D7h)]  
00007FF67D8AA655  call        __CheckForDebuggerJustMyCode (07FF67D874108h)  int sum = add(1, 2);
00007FF67D8AA65A  mov         edx,2  
00007FF67D8AA65F  mov         ecx,1  
00007FF67D8AA664  call        add (07FF67D87584Bh)  
00007FF67D8AA669  mov         dword ptr [sum],eax  return 0;
00007FF67D8AA66C  xor         eax,eax  
}

在汇编代码中,程序暂停在第 14 行(00007FF67D8AA65A mov edx,2)。后面的两行是传入参数的过程,其中,edx是数据寄存器,常用于存储一些大于 AX 寄存器的 16 位数和 32 位数的运算中的高位数。在函数调用中, edx 寄存器用于存储第一个参数值。ecx是计数寄存器,常用于存储循环计数器和移位操作的计数器。在函数调用中, ecx 寄存器用于存储第二个参数值。通过这两行传入的值可以看出,调用函数时,参数入栈时从右往左。
汇编行00007FF67D8AA664 call add (07FF67D87584Bh)用于跳转到待调用的函数内,但这里需要注意的是,地址07FF67D87584Bh并不是待调用的函数的地址,该代码会执行到下面这一行:

00007FF67D87584B  jmp         add (07FF67D8AA5C0h)  

这里的地址07FF67D8AA5C0h才是真正待调用函数的地址。下面即进入被调用函数内部:

int add(int a, int b)
{
00007FF67D8AA5C0  mov         dword ptr [rsp+10h],edx  
00007FF67D8AA5C4  mov         dword ptr [rsp+8],ecx  
00007FF67D8AA5C8  push        rbp  
00007FF67D8AA5C9  push        rdi  
00007FF67D8AA5CA  sub         rsp,108h  
00007FF67D8AA5D1  lea         rbp,[rsp+20h]  
00007FF67D8AA5D6  mov         rdi,rsp  
00007FF67D8AA5D9  mov         ecx,42h  
00007FF67D8AA5DE  mov         eax,0CCCCCCCCh  
00007FF67D8AA5E3  rep stos    dword ptr [rdi]  
00007FF67D8AA5E5  mov         ecx,dword ptr [rsp+128h]  
00007FF67D8AA5EC  lea         rcx,[__81FC6F77_main2@cpp (07FF67D9E41D7h)]  
00007FF67D8AA5F3  call        __CheckForDebuggerJustMyCode (07FF67D874108h)  int sum = a + b;
00007FF67D8AA5F8  mov         eax,dword ptr [b]  
00007FF67D8AA5FE  mov         ecx,dword ptr [a]  
00007FF67D8AA604  add         ecx,eax  
00007FF67D8AA606  mov         eax,ecx  
00007FF67D8AA608  mov         dword ptr [sum],eax  return sum;
00007FF67D8AA60B  mov         eax,dword ptr [sum]  
}

这段汇编代码的第 2 行到第 15 行之间是对该函数的栈初始化工作,由编译器自动添加。其中 rsp ( 32 位程序中是 esp ) 、rbp ( 32 位程序中是 ebp )、rdi ( 32 位程序中是 edi )是常用的寄存器:
rsp 为栈指针,常用来指向栈顶。上面汇编代码中第 6 行00007FF67D8AA5CA sub rsp,108h的意思是将栈顶指针往上移动 108h Byte。这个区域为间隔空间,将被调用的 add 函数与 main 函数的栈区域隔开一段距离,同时还要预留出存储局部变量的内存区域。
rbp 为基址指针,常用来指向栈底。
rdi 为目的变址寄存器。
上面汇编代码的第 17 行到第 21 行之间是进行两数相加的逻辑操作。
执行到第最后一行后打开寄存器查看器( VS2017 查看方法:调试–>窗口–>寄存器),可以查看到如下值:

RAX = 0000000000000003 RBX = 0000000000000000 RCX = 0000000000000003 RDX = 0000000000000002 RSI = 0000000000000000 RDI = 0000005BD30FFA58 R8  = 0000020993014F70 R9  = 0000005BD30FF954 R10 = 0000000000000013 R11 = 00000209930242E0 R12 = 0000000000000000 R13 = 0000000000000000 R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF67D8AA60B RSP = 0000005BD30FF950 RBP = 0000005BD30FF970 EFL = 00000206 0x0000005BD30FF974 = 00000003 

查看寄存器 RDI 的内存值( VS2017 查看方法:调试–>窗口–>内存->内存1):

0000005bd30ffb78 0000005bd30ffa90 00007ff67d8aa669 00007ff600000001 cccccccc00000002 cccccccccccccccc cccccccccccccccc cccccccccccccccc cccccccccccccccc cccccccccccccccc cccccccccccccccc

其中第三个值 00007ff67d8aa669 是 main 函数中调用该函数后的下一行汇编代码。
至此,整个调用过程结束。

面试题6:怎样判断两个浮点数是否相等

由于浮点数存入时有可能因为四舍五入而造成精度损失,所以两个浮点数直接用==操作符进行比较很可能会得到不符合预期的结果。
浮点数的比较应该使用如下方式:
对于浮点数而言比较合适的精度为:0.000001
对于双精度浮点数而言比较合适的精度为:0.0000000000000001
因此可以定义两个宏:

#define ACCURACY_F 1e-6
#define ACCURACY_D 1e-16

判断浮点数是否等于 0 :
float 类型:if(fabs(f) <= ACCURACY_F );
double 类型:if(fabs(d) <= ACCURACY_D);
判断两个浮点数是否相等:
float 类型:if(fabs(f1 - f2) <= ACCURACY_F);
double 类型:if(fabs(d1 - d2) <= ACCURACY_D);

http://www.lryc.cn/news/292026.html

相关文章:

  • AI的安全应答之道
  • 【昕宝爸爸小模块】日志系列之什么是分布式日志系统
  • 如何在淘宝和Shopee上进行选品:策略和原则
  • C++/数据结构:二叉搜索树的实现与应用
  • C++引用、内联函数、auto关键字介绍以及C++中无法使用NULL的原因
  • RabbitMQ之三种队列之间的区别及如何选型
  • 【ArcGIS微课1000例】0099:土地利用变化分析
  • 学习鸿蒙基础(2)
  • 2024年美国大学生数学建模竞赛思路与源代码【2024美赛C题】
  • Windows11搭建GPU版本PyTorch环境详细过程
  • Springboot项目基础配置:小白也能快速上手!
  • 20240127在ubuntu20.04.6下配置whisper
  • C# 递归执行顺序
  • go 实现暴力破解数独
  • go语言-字符串处理常用函数
  • DevOps落地笔记-05|非功能需求:如何有效关注非功能需求
  • vs 撤销本地 commit 并保留更改
  • 深度解读NVMe计算存储协议-1
  • CHS_06.2.3.4_2+用信号量实现进程互斥、同步、前驱关系
  • Web实战丨基于Django的简单网页计数器
  • mysql8安装基础操作(一)
  • MIT6.5830 实验0
  • 【简便方法和积累】pytest 单元测试框架中便捷安装插件和执行问题
  • Zabbix数据库分离与邮件报警
  • mybatisplus-多数据源配置
  • 微信小程序(二十八)网络请求数据进行列表渲染
  • ubuntu22.04 安装conda
  • W801学习笔记十:HLK-W801制作学习机/NES游戏机(总结)
  • 《HTML 简易速速上手小册》第6章:HTML 语义与结构(2024 最新版)
  • 分析HarmonyOS应用/服务的CPU活动性能