个人简历word模板,优化网站哪个好,wdcp装wordpress502,优化大师卸载不了目录
算术运算与赋值
编译器常用的两种优化方案
常量传播
常量折叠
加法
Debug编译选项组下编译后的汇编代码分析
Release开启02执行效率优先
减法
Release版下优化和加法一致#xff0c;不再赘述
乘法
除法
算术结果溢出
自增和自减
关系运算与逻辑运算
JCC指…目录
算术运算与赋值
编译器常用的两种优化方案
常量传播
常量折叠
加法
Debug编译选项组下编译后的汇编代码分析
Release开启02执行效率优先
减法
Release版下优化和加法一致不再赘述
乘法
除法
算术结果溢出
自增和自减
关系运算与逻辑运算
JCC指令
位运算 算术运算与赋值
算术运算包括加法、减法、乘法和除法也称为四则运算。
赋值运算类似于数学中的“等于”是将一个内存空间中的数据传递到另一个内存空间。因为内存没有处理器那样的控制能力所以各个内存单元之间是无法直接传递数据的必须通过处理器访问并中转以实现两个内存单元之间的数据传输。 编译器常用的两种优化方案
在编译过程中编译器常常会采用“常量传播”和“常量折叠”的方案对代码中的变量与常量进行优化
常量传播 将编译期间可计算出结果的变量转换成常量这样就减少了变量的使用
常量折叠 当出现多个常量进行计算且编译器可以在编译期间计算出结果时源码中所有的常量计算都将被计算结果代替 如果在程序的逻辑中声明的变量没有被修改过而且上下文中不存在针对此变量的取地址和间接访问操作那么这个变量就等价于常量编译器就认为可以删除这个变量直接用常量代替。使用常量的好处是可以生成立即数寻址的目标代码常量作为立即数成为指令的一部分从而减少了内存的访问次数。 加法
加法运算对应的汇编指令为ADD。在执行加法运算时不同的操作数对应的转换指令不同编译器会根据优化方式选择最佳的匹配方案。在编译器中常用的优化方案有如下两种。
生成文件占用空间最少。执行效率最快。
在VS中Release编译选项组的默认选项为02选项——执行效率最快。在Debug编译选项组中使用的是OdZI选项此选项使编译器产生的一切代码都以便于调试为根本前提甚至为了便于单步跟踪以及源码和目标代码块的对应阅读不惜增加冗余代码。当然也不是完全放弃优化在不影响调试的前提下会尽可能地进行优化。 Debug编译选项组下编译后的汇编代码分析
源码
#includestdio.hint main()
{int n1 0;int n2 0;// 变量常量n1 n1 1;// 常量常量n1 1 2;// 变量变量n1 n1 n2;printf(n1%d\n, n1);return 0;
}
反汇编分析 归纳
两个常量相加编译期间就会计算出结构有变量参与变量取值存入寄存器相加后通过寄存器存入变量 Release开启02执行效率优先
开启02选项后编译出来的汇编代码会有较大的变化。由于效率优先编译器会将无用代码去除并对可合并代码进行归并处理。
例如在代码清单4-1中“n1 n1 1”这样的代码将被删除因为在其后又重新对变量n1进行了赋值操作而在此之前没有对变量n1做任何访问所以编译器判定此句代码是可被删除的。 减法
计算机中减法是通过加法实现的减正等于加负负数可以使用反码来代替
源码
#includestdio.hint 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两种。由于乘法指令的执行周期较长在编译过程中编译器会先尝试将乘法转换成加法或使用移位等周期较短的指令。当它们都不可转换时才会使用乘法指令。
源码
#includestdio.hint 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中使用“”“--”来实现自增和自减操作。自增和自减有两种定义
一种为自增自减运算符在语句块之后则先执行语句块再执行自增自减另一种恰恰相反自增自减运算符在语句块之前则先执行自增和自减再执行语句块。通常自增和自减是被拆分成两条汇编指令语句执行的
源码
#includestdio.hint 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;
}
反汇编 先将自增自减运算进行分离然后根据运算符的位置来决定执行顺序 。 将 原 语 句 块 “n15 n1”分解为“n25n1”和“n1n11”这样就实现了先参与语句块运算再自增1。同理前缀的拆分过程只是执行顺序做了替换先将自身加1再参与表达式运算。在识别过程中后缀必然会保存计算前的变量值在表达式计算完成后才取出之前的值加1这是个显著特点。 关系运算与逻辑运算
或比较运算符||左右的语句的结果如果有一个值为真则返回真值如果都为假则返回假值。与比较运算符左右的语句的结果如果有一个值为假则返回假值如果都为真值则返回真值。非改变运算符后面语句的真假结果如果该语句的结果为真值则返回假值如果为假值则返回真值。 JCC指令
通常情况下这些条件跳转指令都与CMP和TEST匹配出现但条件跳转指令检查的是标记位。因此在有修改标记位的代码处也可以根据需要使用条件跳转指令修改程序流程。 位运算
二进制数据的运算称为位运算位运算操作符如下
“”左移运算最高位左移到CF中最低位零“”右移运算最高位不变最低位右移到CF中。“|”位或运算在两个数的相同位上只要有一个为1则结果 为1。“”位与运算在两个数的相同位上只有同时为1时结果才 为1。“^”异或运算在两个数的相同位上当两个值相同时为0不同时为1。“~”取反运算将操作数每一位上的1变00变1。 有待提升之处 1. JCC指令位运算的反汇编指令不够熟练 2.除法的优化原理涉及复杂的数学知识还没了解