深度剖解“IF表达式”底层实现过程
2017-07-20 22:15
155 查看
“if表达式”绝大多数c/c++以及其他编程语言的编程人员来说一点都不陌生,几乎每天写代码都会用到这个条件表达式!大多数程序员都知道if就是用来判断某一条件是否成立的,但是你知道if语句底层CPU是怎样执行的吗?编译器会把if表达式翻译成怎样的汇编代码吗?
补充一个问题:刚入编程这行业的新手容易犯的一个错误,就是在写if语句的时候容易把=(赋值表达式)和==(恒等于)混淆,恒等于的意思是需要表达式成立!是逻辑运算的意思,也就是说需要表达式的结果满足一定条件!
c/c++代码:
int main(void)
{
int Test_ = 5; //申请一个int类型的变量,变量名为Test_
if(Test_ == 5) //判断Test变量里的值是否是5
{
Hold water!
}
else
{
No Hold water!
}
}
汇编代码:
PUSH BP //将a变量入栈
MOV BP,SP //SP寄存器指向BP
MOV AX,101B //把5放进AX寄存器符号位省略
MOV BP,AX //把ax寄存器里面的值存入到BP指向的地址
SUB BP,-0//找到位于栈中变量a的起始位置
CMP BP,AX//与5(101)比较 此时ZF寄存器的值是1
JG No Hold water!//当BP里的值大于5时
JE Hold water!//当BP里的值等于5时
JL No Hold water!//当BP里的值小于5时
JNL No Hold water!//当BP里的值大于等于5时
JNG No Hold water!//当BP里的值小于等于5时
PUSH BP
首先将Test_变量压入栈,这里我要说一下为什么是PUSH BP 而不是 PUSH Test_? 因为BP是基址寄存器(基址指针)它保存了函数处于栈空间的入口地址!因为Test_处于函数的第一个位置所以PUSH BP也就是为Test_分配内存。
MOV BP,SP
将栈顶寄存器SP指向BP 。
MOV AX,101
将5放进ax寄存器,一般的翻译器都会将翻译成十六进制,这里我用二进制表示。
MOV BP,AX
将ax寄存器里面的5存放到BP指向的函数入口地址(也就是Test_的起始地址)为什么不直接把5放进BP指针指向的Test_变量起始地址上?学过汇编的应该都知道,地址寄存器是不能直接访问的,要通过媒介寄存器来访问!这里说的媒介就是一般寄存器,除了一般寄存器两个地址寄存器一样可以进行交互!
SUB BP,-0
把BP基址寄存器偏移地址指向Test_的起始地址
CMP BP,AX
通过CMP指令比较两个数,并根据比较结果设置相应ZF寄存器状态
根据ZF寄存器状态执行相应的指令
JG 大于
JE 等于
JL 小于
JNL 大于等于
JNG 小于等于
以上就是if语句底层执行过程,从分析结果来看,if语句会先处理括号里面的数在判断。
简单来说在if表达式里面做任何运算计算机都会执行
示例
if(Test_ = 3)
{
}
else
{
false
}
因为优先级的关系,在c语言里面被括号括起来的表达式优先级都会提高一个等级!编译器会优先编译括号里面的表达式,在去编译括号外的,也就是说:会先给Test_赋值3在去执行CMP指令
如果是赋值的话汇编代码是如下
MOV BP,011B
CMP BP //只要BP指向的那一块地址内存里的数大于1即执行JG命令
在计算机内部0/1代表高低电平,高电平对应的是开,低电平是关。
也就是说想让某个芯片运行就要向某个芯片发送高电平!
在操作系统中0即逻辑假1即逻辑真,无论是任何一款操作系统Linux,Windows还是安卓和ios都遵循这一逻辑。
最后建议大家在用if语句判断变量是否等于某个常数时可以这样写
if(5 == a)
这样就算少写一个等号在编译时也会报错因为常数不可赋值!
这里说一个关于左值右值的问题:
左值变量右值常数,常数是不能赋值的,因为编译器并没有为其分配内存。
也就是说没有内存的常数并不属于堆栈中,而是属于代码段中。
其实说是常数没有内存其实是占内存的,因为指令也需要内存来存放吖!
这里说下常数有个别名:也称为“立即数”。
立即数是存储于指令集中的并没有单独的内存空间,立即数的最大值是有限的,大小是取决于你电脑中的指令集。
比如arm的指令集是32位的,其中低位存指令、高位存立即数的二进制位。
那么这个立即数的MAX是4099,如果超出最大上限值编译器就会单独在栈中开辟一块空间,来存放这个立即数。
立即数的最大值取决于你计算机指令集的位数大小的一半!不同的处理器指令集对应的位数不同。比如arm的指令集是32位的早期的arm是16位的。
ps:本文章一部分是在地铁等候室里写的,有些地方说的可能不太仔细模糊凌乱!还请谅解,如果有写的不对的地方可以指出来,谢谢!
汇编哪一段代码是博主看着翻译的并不是编译器翻译的结果,因为当时在地铁等候室里没有电脑!
如果有翻译错的地方还请大家指出来。
补充一个问题:刚入编程这行业的新手容易犯的一个错误,就是在写if语句的时候容易把=(赋值表达式)和==(恒等于)混淆,恒等于的意思是需要表达式成立!是逻辑运算的意思,也就是说需要表达式的结果满足一定条件!
c/c++代码:
int main(void)
{
int Test_ = 5; //申请一个int类型的变量,变量名为Test_
if(Test_ == 5) //判断Test变量里的值是否是5
{
Hold water!
}
else
{
No Hold water!
}
}
汇编代码:
PUSH BP //将a变量入栈
MOV BP,SP //SP寄存器指向BP
MOV AX,101B //把5放进AX寄存器符号位省略
MOV BP,AX //把ax寄存器里面的值存入到BP指向的地址
SUB BP,-0//找到位于栈中变量a的起始位置
CMP BP,AX//与5(101)比较 此时ZF寄存器的值是1
JG No Hold water!//当BP里的值大于5时
JE Hold water!//当BP里的值等于5时
JL No Hold water!//当BP里的值小于5时
JNL No Hold water!//当BP里的值大于等于5时
JNG No Hold water!//当BP里的值小于等于5时
PUSH BP
首先将Test_变量压入栈,这里我要说一下为什么是PUSH BP 而不是 PUSH Test_? 因为BP是基址寄存器(基址指针)它保存了函数处于栈空间的入口地址!因为Test_处于函数的第一个位置所以PUSH BP也就是为Test_分配内存。
MOV BP,SP
将栈顶寄存器SP指向BP 。
MOV AX,101
将5放进ax寄存器,一般的翻译器都会将翻译成十六进制,这里我用二进制表示。
MOV BP,AX
将ax寄存器里面的5存放到BP指向的函数入口地址(也就是Test_的起始地址)为什么不直接把5放进BP指针指向的Test_变量起始地址上?学过汇编的应该都知道,地址寄存器是不能直接访问的,要通过媒介寄存器来访问!这里说的媒介就是一般寄存器,除了一般寄存器两个地址寄存器一样可以进行交互!
SUB BP,-0
把BP基址寄存器偏移地址指向Test_的起始地址
CMP BP,AX
通过CMP指令比较两个数,并根据比较结果设置相应ZF寄存器状态
根据ZF寄存器状态执行相应的指令
JG 大于
JE 等于
JL 小于
JNL 大于等于
JNG 小于等于
以上就是if语句底层执行过程,从分析结果来看,if语句会先处理括号里面的数在判断。
简单来说在if表达式里面做任何运算计算机都会执行
示例
if(Test_ = 3)
{
trutl
}
else
{
false
}
因为优先级的关系,在c语言里面被括号括起来的表达式优先级都会提高一个等级!编译器会优先编译括号里面的表达式,在去编译括号外的,也就是说:会先给Test_赋值3在去执行CMP指令
如果是赋值的话汇编代码是如下
MOV BP,011B
CMP BP //只要BP指向的那一块地址内存里的数大于1即执行JG命令
在计算机内部0/1代表高低电平,高电平对应的是开,低电平是关。
也就是说想让某个芯片运行就要向某个芯片发送高电平!
在操作系统中0即逻辑假1即逻辑真,无论是任何一款操作系统Linux,Windows还是安卓和ios都遵循这一逻辑。
最后建议大家在用if语句判断变量是否等于某个常数时可以这样写
if(5 == a)
这样就算少写一个等号在编译时也会报错因为常数不可赋值!
这里说一个关于左值右值的问题:
左值变量右值常数,常数是不能赋值的,因为编译器并没有为其分配内存。
也就是说没有内存的常数并不属于堆栈中,而是属于代码段中。
其实说是常数没有内存其实是占内存的,因为指令也需要内存来存放吖!
这里说下常数有个别名:也称为“立即数”。
立即数是存储于指令集中的并没有单独的内存空间,立即数的最大值是有限的,大小是取决于你电脑中的指令集。
比如arm的指令集是32位的,其中低位存指令、高位存立即数的二进制位。
那么这个立即数的MAX是4099,如果超出最大上限值编译器就会单独在栈中开辟一块空间,来存放这个立即数。
立即数的最大值取决于你计算机指令集的位数大小的一半!不同的处理器指令集对应的位数不同。比如arm的指令集是32位的早期的arm是16位的。
ps:本文章一部分是在地铁等候室里写的,有些地方说的可能不太仔细模糊凌乱!还请谅解,如果有写的不对的地方可以指出来,谢谢!
汇编哪一段代码是博主看着翻译的并不是编译器翻译的结果,因为当时在地铁等候室里没有电脑!
如果有翻译错的地方还请大家指出来。
相关文章推荐
- C++ 实现Mutex(底层用win32) 和 C++中析构过程 和常量性的大坑
- SDWebImage底层实现原理及内部实现过程
- HTTP通信过程底层实现原理
- SDWebImage底层实现原理及内部实现过程
- Android 启动过程的底层实现
- Android 开发过程中 Java 代码中属性 设置 底层实现
- js利用与或运算符优先级实现if else条件判断表达式
- 底层理解函数调用实现过程 栈结构 栈过程
- Nginx if语句加正则表达式实现字符串截断
- Android启动过程的底层实现
- epoll底层实现过程
- pytorch学习笔记(十三):backward过程的底层实现解析
- Linux 网络编程及底层实现过程
- js利用运算符优先级实现if else条件判断表达式
- JDK8中Lambda表达式底层实现浅析(一)
- 【cs229-Lecture2】Gradient Descent 最小二乘回归问题解析表达式推导过程及实现源码(无需迭代)
- SDWebImage底层实现原理及内部实现过程
- 第三章:通话连接的作用以及建立过程之三TelephonyConnectionService.java与RIL.java交互实现通话底层拨号功能
- 利用存储过程实现Oracle的drop table if exists