分析各种表达式求值过程

目录

算术运算与赋值

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

常量传播

常量折叠

加法

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.xdnf.cn/news/145980.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

What is an HTTP Flood DDoS attack?

HTTP 洪水攻击是一种针对 Web 和应用程序服务器的第 7 层分布式拒绝服务 &#xff08;DDoS&#xff09; 攻击。HTTP 洪水攻击通过使用 HTTP GET 或 HTTP POST 请求执行 DDoS 攻击。这些请求是有效的&#xff0c;并且针对可用资源&#xff0c;因此很难防范 HTTP 洪水攻击。 匿名…

你熟悉Docker吗?

你熟悉Docker吗&#xff1f; 文章目录 你熟悉Docker吗&#xff1f;快速入门Docker安装1.卸载旧版2.配置Docker的yum库3.安装Docker4.启动和校验5.配置镜像加速5.1.注册阿里云账号5.2.开通镜像服务5.3.配置镜像加速 部署MySQL镜像和容器命令解读 Docker基础常用命令数据卷数据卷…

在SpringBoot中利用Redis实现互斥锁

在SpringBoot中利用Redis实现互斥锁 基本知识 前提条件&#xff0c;有一个能够在Springboot中使用Redis的项目&#xff0c;或者能够直接开也行 为什么要实现互斥锁&#xff1a;当我们利用Redis存储热点数据时&#xff0c;突然就过期失效或者被删除了&#xff0c;导致大量请求同…

Vue以及整合ElementUI

初始化vue项目 #vue 脚手架使用 webpack 模板初始化一个 appname 项目 vue init webpack appname启动 vue 项目 #项目的 package.json 中有 scripts&#xff0c;代表我们能运行的命令 npm start npm run dev #启动项目 npm run build&#xff1a;将项目打包项目结构 运行流程…

顺序表(7.24)

1.线性表 线性表 &#xff08; linear list &#xff09; 是 n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一…

ssm+vue的4S店预约保养管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的4S店预约保养管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…

aarch64 平台 musl gcc 工具链手动编译方法

目标 手动编译一个 aarch64 平台的 musl gcc 工具链 musl libc 与 glibc、uclibc 等,都是 标准C 库, musl libc 是基于系统调用之上的 标准C 库,也就是用户态的 标准C 库。 musl libc 轻量、开源、免费,是一些 操作系统的选择,当前 Lite-OS 与 RT-Smart 等均采用自制的 mu…

【网络原理】初始网络,了解概念

文章目录 1. 网络通信1.1 局域网LAN1.2 广域网WAN 2. 基础概念2.1 IP2.2 端口号 3. 认识协议4. 五元组5. 协议分层5.1 分层的作用5.2 OSI七层模型5.3 TCP/IP五层&#xff08;四层&#xff09;模型 6. 封装和分用 1. 网络通信 计算机与计算机之间是互相独立&#xff0c;是独立模…

配置pytorchGPU虚拟环境-python3.7

cuda版本的pytorch包下载地址戳这里 winR->输入cmd->输nvcc -V回车 cuda 11.0 输入以下命令来查找 CUDA 的安装路径&#xff1a; Windows: where nvcc 输入以下命令来查找 cuDNN 的版本号&#xff1a; Windows: where cudnn* cuDNN 8.0 本机安装的是cuda 11.0&…

Python海洋专题五之水深地形图海岸填充

Python海洋专题五之水深地形图海岸填充 海洋与大气科学 上期读取nc水深文件&#xff0c;并出图 但是存在一些不完美&#xff0c;本期修饰 本期内容 障眼法&#xff1a;把大于零的数据填充为陆地的灰色&#xff1b; 把等于零的数据画等深线为陆地和海洋的分界线&#xff01;…

YOLOv8改进算法之添加CA注意力机制

1. CA注意力机制 CA&#xff08;Coordinate Attention&#xff09;注意力机制是一种用于加强深度学习模型对输入数据的空间结构理解的注意力机制。CA 注意力机制的核心思想是引入坐标信息&#xff0c;以便模型可以更好地理解不同位置之间的关系。如下图&#xff1a; 1. 输入特…

C语言数据结构之排序整合与比较(冒泡,选择,插入,希尔,堆排序,快排及改良,归并排序,计数排序)

前言&#xff1a;排序作为数据结构中的一个重要模块&#xff0c;重要性不言而寓&#xff0c;我们的讲法为下理论掌握大致的算法结构&#xff0c;再上代码及代码讲解&#xff0c;助你一臂之力。 一&#xff0c;冒泡 冒泡排序应该是大家学习以来第一个认识的排序方法&#xff0…

【小沐学前端】Node.js实现UDP和Protobuf 通信(protobuf.js)

文章目录 1、简介1.1 node1.2 Protobuf 2、下载和安装2.1 node2.2 Protobuf 3、node 代码示例3.1 HTTP3.2 UDP单播3.4 UDP广播 4、Protobuf 代码示例4.1 例子:awesome.proto 结语 1、简介 1.1 node Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。 Node.js 是一个开源…

案例突破——再探策略模式

再探设计模式 一、背景介绍二、 思路方案三、过程1. 策略模式基本概念2. 策略模式类图3. 策略模式基本代码策略类抽象策略类Context类客户端 4. 策略模式还可以进行优化的地方5. 对策略模式的优化&#xff08;配置文件反射&#xff09; 四、总结五、升华 一、背景介绍 在做项目…

FPGA设计时序约束二、输入延时与输出延时

目录 一、背景 二、set_input_delay 2.1 set_input_delay含义 2.2 set_input_delay参数说明 2.3 使用样例 三、set_output_delay 3.1 set_output_delay含义 3.2 set_output_delay参数说明 3.3 使用样例 四、样例工程 4.1 工程代码 4.2 时序报告 五、参考资料 一、…

敏感性分析一览

敏感性分析 SobolMorrisFourier Amplitude Sensitivity Test (FAST)Random Balance Designs - Fourier Amplitude Sensitivity Test (RBD-FAST)Delta Moment-Independent MeasureDerivative-based Global Sensitivity Measure (DGSM)Fractional Factorial Sensitivity Analysis…

生产者、消费者问题

线程六个状态&#xff1a; public enum State {/*** 新生*/NEW,/*** 运行*/RUNNABLE,/***阻塞*/BLOCKED,/*** 等待*/WAITING,/*** 超时等待*/TIMED_WAITING,/**死亡**/TERMINATED;} synchronized和lock的区别 1、synchronized是关键字&#xff0c;lock是类 2、synchronized全自…

电压提前/滞后电路 —— 电赛综测备赛

电容可以让交流波形提前或滞后&#xff0c;不过很多人不知道用法 电压滞后电路 我们从中间输出给示波器 波形&#xff08;红色&#xff09; 电容越大电阻越大&#xff0c;波形越滞后 电压提前电路 波形&#xff08;红色&#xff09;提前 电容越小电阻越小&#xff0c;波形…

【Linux】——基操指令(二)

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 目录 前言 man指令 cp 指令 mv指令 echo指令 cat指令 more指令 less指令 head和tail指令 head指令 tail指令 前言 上篇文章给大家讲解了Linux环境下的一点基操指令&#xf…

AVL树的模拟实现(c++)

目录 搜索二叉树对于搜索查询来说是非常快的&#xff0c;但是它有着致命的缺陷&#xff0c;如果插入的数据是有序的&#xff0c;那么它的结构就会退化成单链表&#xff0c;这对于搜索查询来说是非常不利的&#xff0c;因此为了解决搜索树的缺陷&#xff0c;弥补它的不足&#x…