分析各种表达式求值过程

目录

算术运算与赋值

编译器常用的两种优化方案

常量传播

常量折叠

加法

Debug编译选项组下编译后的汇编代码分析

Release开启02执行效率优先

减法

Release版下优化和加法一致,不再赘述

乘法

除法

算术结果溢出

自增和自减

关系运算与逻辑运算

JCC指令

位运算


算术运算与赋值

算术运算包括加法、减法、乘法和除法,也称为四则运算。

赋值运算类似于数学中的“等于”,是将一个内存空间中的数据传递到另一个内存空间。因为内存没有处理器那样的控制能力,所以各个内存单元之间是无法直接传递数据的,必须通过处理器访问并中转,以实现两个内存单元之间的数据传输。

编译器常用的两种优化方案

在编译过程中,编译器常常会采用“常量传播”和“常量折叠”的方案对代码中的变量与常量进行优化

常量传播

        将编译期间可计算出结果的变量转换成常量,这样就减少了变量的使用

常量折叠

        当出现多个常量进行计算,且编译器可以在编译期间计算出结果时,源码中所有的常量计算都将被计算结果代替

        如果在程序的逻辑中,声明的变量没有被修改过,而且上下文中不存在针对此变量的取地址和间接访问操作,那么这个变量就等价于常量,编译器就认为可以删除这个变量,直接用常量代替。使用常量的好处是可以生成立即数寻址的目标代码,常量作为立即数成为指令的一部分,从而减少了内存的访问次数。

加法

加法运算对应的汇编指令为ADD。在执行加法运算时,不同的操作数对应的转换指令不同,编译器会根据优化方式选择最佳的匹配方案。在编译器中常用的优化方案有如下两种。

  1. 生成文件占用空间最少。
  2. 执行效率最快。

在VS中,Release编译选项组的默认选项为02选项——执行效率最快。在Debug编译选项组中,使用的是Od+ZI选项,此选项使编译器产生的一切代码都以便于调试为根本前提,甚至为了便于单步跟踪以及源码和目标代码块的对应阅读,不惜增加冗余代码。当然也不是完全放弃优化,在不影响调试的前提下,会尽可能地进行优化。

Debug编译选项组下编译后的汇编代码分析

源码

#include<stdio.h>int main()
{int n1 = 0;int n2 = 0;// 变量+常量n1 = n1 + 1;// 常量+常量n1 = 1 + 2;// 变量+变量n1 = n1 + n2;printf("n1=%d\n", n1);return 0;
}

反汇编分析

归纳:

  1. 两个常量相加:编译期间就会计算出结构
  2. 有变量参与:变量取值存入寄存器相加后通过寄存器存入变量

Release开启02执行效率优先

开启02选项后,编译出来的汇编代码会有较大的变化。由于效率优先,编译器会将无用代码去除,并对可合并代码进行归并处理。

例如在代码清单4-1中,“n1 = n1 + 1;”这样的代码将被删除,因为在其后又重新对变量n1进行了赋值操作,而在此之前没有对变量n1做任何访问,所以编译器判定此句代码是可被删除的。

减法

计算机中,减法是通过加法实现的,减正等于加负,负数可以使用反码来代替;

源码

#include<stdio.h>int main(int argc, char* argv[])
{int n1 = argc;int n1;int n2 = 0;scanf("%d", &n2);n1 = n1 - 100;n1 = n1 + 5 - n2;printf("n1 = %d \r\n", n1);return 0;
}

反汇编

Release版下优化和加法一致,不再赘述

乘法

乘法运算对应的汇编指令分为有符号imul和无符号mul两种。由于乘法指令的执行周期较长,在编译过程中,编译器会先尝试将乘法转换成加法,或使用移位等周期较短的指令。当它们都不可转换时,才会使用乘法指令。

源码

#include<stdio.h>int main(int argc, char* argv[])
{int n1 = argc;int n2 = argc;// 变量乘常量printf("n1 * 15 = %d\n", n1 * 15);// 变量乘常量(2的幂)printf("n1 * 16 = %d\n", n1 * 16);// 两个常量相乘printf("2 * 2 = %d\n", 2 * 2);printf("n2 * 4 + 5 = %d\n", n2 * 4 + 5);// 混合运算printf("n1 * n2 = %d\n", n1 * n2);// 两变量相乘return 0;
}

反汇编

有符号数乘以常量值,且这个常量非2的幂,会直接使用有符号乘法imul指令或者左移加减运算进行优化。

当常量值为2的幂时,编译器会采用执行周期短的左移运算代替执行周期长的乘法指令。由于任何十进制数都可以转换成二进制数表示,在二进制数中乘以2就等同于所有位依次向左移动1位。
乘法运算与加法运算的结合编译器采用LEA指令处理。LEA语句的目的并不是获取地址。

除了两个未知变量的相乘无法优化外,其他形式的乘法运算都可以进行优化处理。如果运算表达式中有一个常量值,则此时编译器会首先匹配各类优化方案,最后对不符合优化方案的运算进行调整。
无符号乘法的原理与之相同

除法

        除法运算对应的汇编指令分为有符号idiv和无符号div两种。除法指令的执行周期较长,效率也较低,所以编译器会想尽办法用其他运算指令代替除法指令。C++中的除法和数学中的除法不同,在C++中,除法运算不保留余数,有专门求取余数的运算(运算符为%),也称之为取模运算。对于整数除法,C++的规则是仅保留整数部分,小数部分完全舍弃。

编译器在除法的优化涉及到高深的数学知识,暂且放放

算术结果溢出

当数据大小超过存储空间时,就会发生溢出,溢出的数据不会保留;

进位:无符号数超出存储范围叫作进位。因为没有符号位,不会破坏数据,而进位的1位数据会被进位标志为CF保存。而在标志位CF中,可通过查看进位标志位CF,检查数据是否进位

溢出:有符号数超出存储范围叫作溢出,由于数据进位,从而破坏了有符号数的最高位——符号位。只有有符号数才有符号位,所以溢出只针对有符号数。可查看溢出标志位OF,检查数据是否溢出。OF的判定规则很简单,如果参与加法计算的数值符号一致,而计算结果符号不同,则判定OF成立,其他都不成立。

自增和自减

C++中使用“++”“--”来实现自增和自减操作。自增和自减有两种定义:

  • 一种为自增自减运算符在语句块之后,则先执行语句块,再执行自增自减;
  • 另一种恰恰相反,自增自减运算符在语句块之前,则先执行自增和自减,再执行语句块。
  • 通常,自增和自减是被拆分成两条汇编指令语句执行的

源码

#include<stdio.h>int main(int argc, char* argv[])
{int n1 = argc;int n2 = argc;n2 = 5 + (n1++);n2 = 5 + (++n1);n1 = 5 + (n2--);n1 = 5 + (--n2);return 0;
}

反汇编

先将自增自减运算进行分离,然后根据运算符的位置来决定执行顺序 。 将 原 语 句 块 “n1=5+
(n1++);”分解为“n2=5+n1;”和“n1=n1+1”,这样就实现了先参与语句块运算,再自增1。同理,前缀++的拆分过程只是执行顺序做了替换,先将自身加1,再参与表达式运算。在识别过程中,后缀++必然会保存计算前的变量值,在表达式计算完成后,才取出之前的值加1,这是个显著特点。
 

关系运算与逻辑运算

  • 或:比较运算符||左右的语句的结果,如果有一个值为真,则返回真值;如果都为假,则返回假值。
  • 与:比较运算符&&左右的语句的结果,如果有一个值为假,则返回假
  • 值;如果都为真值,则返回真值。
  • 非:改变运算符!后面语句的真假结果,如果该语句的结果为真值,则返回假值;如果为假值,则返回真值。

JCC指令

通常情况下,这些条件跳转指令都与CMP和TEST匹配出现,但条件跳转指令检查的是标记位。因此,在有修改标记位的代码处,也可以根据需要使用条件跳转指令修改程序流程。

位运算

二进制数据的运算称为位运算,位运算操作符如下:

  1. “<<”:左移运算,最高位左移到CF中,最低位零
  2. “>>”:右移运算,最高位不变,最低位右移到CF中。
  3. “|”:位或运算,在两个数的相同位上,只要有一个为1,则结果 为1。
  4. “&”:位与运算,在两个数的相同位上,只有同时为1时,结果才 为1。
  5. “^”:异或运算,在两个数的相同位上,当两个值相同时为0,不同时为1。
  6. “~”:取反运算,将操作数每一位上的1变0,0变1。

有待提升之处:

1. JCC指令,位运算的反汇编指令不够熟练

2.除法的优化原理涉及复杂的数学知识,还没了解

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

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

相关文章

乐鑫 ESP-Mesh-Lite在windows下的开发环境搭建

ESP-Mesh-Lite的开发环境由于没有官方教程&#xff0c;折腾了好几天。环境搭建主要还是参考ESP-MDF环境搭建&#xff0c;特别注意的是必须要在CMD环境下操作&#xff0c;不能用POWER SHELL。 ESP-Mesh-Lite目前支持到5.1的SDK&#xff0c;当然4.4也是可以用的。首先上Gitee或G…

百度统计配置详细图文教程包含siteId、百度统计AccessToken、百度统计代码获取步骤教程

一、前言 很多网友开发者都不知道百度统计siteId、百度统计token怎么获取&#xff0c;在网上找的教程都是几年前老的教程&#xff0c;因此给大家出一期详细百度统计siteId、百度统计token、百度统计代码获取详细步骤教程。 二、登录到百度统计 1.1 登录到百度统计官网 使用个…

SPSS探索性分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件可在个人主页—…

StarRocks数据导入

1、相关环境 Flink作为当前流行的流式计算框架&#xff0c;在对接StarRocks时&#xff0c;若直接使用JDBC的方式"流式"写入数据&#xff0c;对StarRocks是不友好的&#xff0c;StarRocks作为一款MVCC的数据库&#xff0c;其导入的核心思想还是"攒微批降频率&qu…

SPSS列联表分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件可在个人主页—…

BUUCTF reverse wp 71 - 75

[NPUCTF2020]你好sao啊 int __cdecl main(int argc, const char **argv, const char **envp) {__int64 v3; // rax__int64 v4; // rdx__int64 v5; // raxsize_t v6; // rax__int64 v7; // rax__int64 v8; // rdx__int64 v9; // rax__int64 v11; // rdx__int64 v12; // raxchar …

【STM32】IAP升级01 bootloader实现以及APP配置(主要)

APP程序以及中断向量表的偏移设置 前言 通过之前的了解 之前的了解&#xff0c;我们知道实现IAP升级需要两个条件&#xff1a; 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始&#xff1b; 2.APP程序的中断向量表相应的移动&#xff0c;移动的偏移量为 x&#xff…

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…

cloudCompare教程:一、可视化、点、线编辑

依据高度等准则(都在Scalar Fields中)渲染点云&#xff08;首先要打开Tools -> Projection -> Export coordinate to SF&#xff09; 在上述准则之外的&#xff0c;设置为不显示&#xff1a; 软件的显示设置&#xff08;首先打开右边的彩色柱状图&#xff0c;点击左边属性…

ECharts多个数据视图进行自适应大小的解决方案

项目场景&#xff1a; 在制作数据视图时经常会遇到多个数据视图的情况&#xff0c;在多个数据视图的情况下做自适应是比较麻烦的&#xff0c;这里就详细的分析一下该如何去制作&#xff0c;分享一下我的解决办法及思路。 定义 DOM 容器 这里需要注意一个地方&#xff0c;在定…

idea Springboot 校园助学贷款系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 校园助学贷款系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统 具有完整的源代码和数据库&…

2023.09.30使用golang1.18编译Hel10-Web/Databasetools的windows版

#Go 1.21新增的 log/slog 完美解决了以上问题&#xff0c;并且带来了很多其他很实用的特性。 本次编译不使用log/slog 包 su - echo $GOPATH ;echo $GOROOT; cd /tmp; busybox wget --no-check-certificate https://go.dev/dl/go1.18.linux-amd64.tar.gz;\ which tar&&am…

C++核心编程--继承篇

4.6、继承 继承是面向对象三大特征之一 有些类与类之间存在特殊的关系&#xff0c;例如下图中&#xff1a; ​ 我们发现&#xff0c;定义这些类的定义时&#xff0c;都拥有上一级的一些共性&#xff0c;还有一些自己的特性。那么我们遇到重复的东西时&#xff0c;就可以考虑使…

用go实现http服务端和请求端

一、概述 本文旨在学习记录下如何用go实现建立一个http服务器&#xff0c;同时构造一个专用格式的http客户端。 二、代码实现 2.1 构造http服务端 1、http服务处理流程 基于HTTP构建的服务标准模型包括两个端&#xff0c;客户端(Client)和服务端(Server)。HTTP 请求从客户端…

泰国数字加密平台Bitkub创始人到访上海和数集团

2023年9月21日&#xff0c;泰国数字加密货币交易平台Bitkub创始人兼首席执行官&#xff08;CEO&#xff09;Jirayut Srupsrisopa (Topp)先生到访上海和数集团总部。董事长唐毅先生热情会见了来宾&#xff0c;双方进行了友好深入的交流。 和数集团国际部经理晋松&#xff1b;苏州…

BUUCTF reverse wp 76 - 80

[CISCN2018]2ex 四处游走寻找关键代码 int __fastcall sub_400430(int a1, unsigned int a2, int a3) {unsigned int v3; // $v0int v4; // $v0int v5; // $v0int v6; // $v0unsigned int i; // [sp8h] [8h]unsigned int v9; // [sp8h] [8h]int v10; // [spCh] [Ch]v10 0;for…

在 Python 中列出虚拟环境

文章目录 在Python中列出虚拟环境使用lsvirtualenv命令使用Conda命令使用workon命令 总结 虚拟环境是一个独立的环境&#xff0c;我们可以在其中安装库、包、脚本和Python解释器。如果你的项目需要不同版本的库或Python解释器&#xff0c;你可以为每个项目创建单独的虚拟环境。…

2.索引操作

1. 创建索引 创建索引就等于创建数据库&#xff0c;ES使用put操作创建索引&#xff0c;我们创建一个students的索引&#xff0c;只需要发生put请求&#xff1a;http://127.0.0.1:9200/students 2. 查看索引 2.1 查看所有索引&#xff1a; 使用http://127.0.0.1:9200/_cat/ind…

Firefox 开发团队对 Vue 3 进行优化效果显著

Mozilla 官方博客近日发表文章《Faster Vue.js Execution in Firefox》&#xff0c;介绍了 Firefox 开发团队对 Vue 3 进行的优化。 文章写道&#xff0c;在使用 Speedometer 3 对 Firefox 进行基准测试时&#xff0c;他们发现 Vue.js test 的测试结果从 Vue 2 升级到 Vue 3 后…

ElasticSearch 同步数据变少了

一、前言 这几天对接ES遇到几个坑&#xff0c;我们将一张库存表同步到ES发现Docs Count和我们表中的数据对不上&#xff0c;需要加上Docs deleted才对得上&#xff0c;也不知道批量写入数据为什么有些数据就会成 Docs deleted。 二、ID和版本号 ES中每一个Document都有一个_…