[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字

本文主要是介绍[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[简介]
常用网名: 猪头三
出生日期: 1981.XX.XX
QQ: 643439947
个人网站: 80x86汇编小站 https://www.x86asm.org
编程生涯: 2001年~至今[共22年]
职业生涯: 20年
开发语言: C/C++、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python
开发工具: Visual Studio、Delphi、XCode、Eclipse、C++ Builder
技能种类: 逆向 驱动 磁盘 文件
研发领域: Windows应用软件安全/Windows系统内核安全/Windows系统磁盘数据安全/macOS应用软件安全
项目经历: 磁盘性能优化/文件系统数据恢复/文件信息采集/敏感文件监测跟踪/网络安全检测

[序言]
最近在努力地学习C++20的相关知识点, 给自己订下一个小目标: 把自身已掌握的陈旧C++98, C++03逐步升级到C++20. 以适应现代C++开发的要求. 在学习和复习的过程中, 顺便记录疑惑点.

[新增][constexpr]
C++11引入了constexpr关键字来声明变量, 这种变量可在编译时求值并最终生成一个常量. 由于不会产生运行时开销, 所以编译能执行额外的优化来提高应用程序的性能.

[什么是"编译时", 什么是"运行时"]
要了解"constexpr"的作用前提是, 一定要弄清楚两个概念细节"编译时"和"运行时".

"编译时": 分析和解析源代码文件的过程, 比如语法检查, 词法分析, 优化代码...
"运行时": 程序的运行过程中

理解这个两个概念之后就很好理解下面的代码了.

比如想要一个求平方函数constexpr_fun_Square()在"编译时"就运行起来, 那么就需要在函数前面添加"constexpr"关键字

// 编译时执行函数 (求平方)
constexpr int constexpr_fun_Square(int int_param_X) {return  int_param_X * int_param_X ;
}

比如想要一个求平方函数fun_Square()在程序启动之后才能执行, 那么就按照正常的函数声明即可.

// 运行时执行函数 (求平方)
int fun_Square(int int_param_X) {return  int_param_X * int_param_X ;
}

[在代码中适当的引用"编译时"代码, 为什么会提升应用程序的性能呢?]
要理解这个核心, 可以通过逆向分析, 观察"编译时"代码和"运行时"代码的差异.

1> 首先启动相关的C/C++的开发工具(我使用的是 C++ Builder 12), 创建一个C++命令控制台程序, 把如下代码整合到新建的项目中, 然后编译运行.

#include <iostream>
#include <locale>// 编译时执行函数 (求平方)
constexpr int constexpr_fun_Square(int int_param_X) {return  int_param_X * int_param_X ;
}// 运行时执行函数 (求平方)
int fun_Square(int int_param_X) {return  int_param_X * int_param_X ;
}int _tmain(int argc, _TCHAR* argv[])
{// 1> 把编译时结果赋值给编译时变量constexpr int int_Square_A = constexpr_fun_Square(5) ;// 2> 把运行时结果赋值给运行时变量int int_Square_C = fun_Square(5);// 3> 把编译时结果赋值给运行时变量int int_Square_B = constexpr_fun_Square(5) ;}

2> 对"constexpr int int_Square_A = constexpr_fun_Square(5) ;"行下断点之后, 以Debug模式启动运行
3> 当程序被断下来之后, 就切换到汇编指令模式, 得到如下汇编代码.

File1.cpp.15: int _tmain(int argc, _TCHAR* argv[])
005814F0 55               push ebp
005814F1 89E5             mov ebp,esp
005814F3 83EC28           sub esp,$28
005814F6 8B450C           mov eax,[ebp+$0c]
005814F9 8B4D08           mov ecx,[ebp+$08]
005814FC BA05000000       mov edx,$00000005
00581501 C745FC00000000   mov [ebp-$04],$00000000
File1.cpp.18: constexpr int int_Square_A = constexpr_fun_Square(5) ;
00581508 C745F819000000   mov [ebp-$08],$00000019 // 1> "编译时"得到了优化
File1.cpp.21: int int_Square_C = fun_Square(5);
0058150F C7042405000000   mov [esp],$00000005
00581516 8945EC           mov [ebp-$14],eax
00581519 894DE8           mov [ebp-$18],ecx
0058151C 8955E4           mov [ebp-$1c],edx
0058151F E8ACFFFFFF       call fun_Square(int)  // 2> 正常调用, 因为fun_Square()函数是运行时执行
00581524 B905000000       mov ecx,$00000005
00581529 8945F4           mov [ebp-$0c],eax
File1.cpp.24: int int_Square_B = constexpr_fun_Square(5) ;
0058152C C7042405000000   mov [esp],$00000005
00581533 894DE0           mov [ebp-$20],ecx
00581536 E80D000000       call constexpr_fun_Square(int) // 3> "编译时"没有优化
0058153B 8945F0           mov [ebp-$10],eax
File1.cpp.26: }

通过观察如上的汇编代码, 惊奇的发现 "constexpr int int_Square_A = constexpr_fun_Square(5) ;" 这段代码并没有调用constexpr_fun_Square()函数, 而是直接赋值, 效果相当于如下写法:

constexpr int int_Square_A = constexpr_fun_Square(5) ;
等价于
const int int_Square_A = 25;
且等价于汇编代码
00581508 C745F819000000   mov [ebp-$08],$00000019

这意味着什么?意味着这个程序运行的时候少了调用constexpr_fun_Square(5) 的环节, 那继续意味着什么? 就是大大提升了程序的运行效率.

[不要开心, 下面一个重要的细节: 3> 把编译时结果赋值给运行时变量]
当程序如果运行到如下代码, 又会发生什么情况?:

// 3> 把编译时结果赋值给运行时变量
int int_Square_B = constexpr_fun_Square(5) ;

找到并观察对应的汇编代码

0058152C C7042405000000   mov [esp],$00000005
00581533 894DE0           mov [ebp-$20],ecx
00581536 E80D000000       call constexpr_fun_Square(int)

结果发现, 不是想象中那么美好,  程序调用(call) constexpr_fun_Square(int)这个函数, 没有提升运行效率,为什么会这样呢?这是因为int_Square_B变量并不是constexpr变量, 因此编译器没有针对它进行"编译时"优化.

[结尾]
这是一个全新的角度来分析和理解constexpr关键字的作用, 只有真正通过逆向观察, 才能有更深地体会, 更容易理解书本上的文字描述. 希望大家喜欢这篇文章, 如果有对文章有更多的疑问, 可以留言, 我会一一认真回复的.

[截图欣赏]

这篇关于[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/450918

相关文章

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类