
AI-摘要
PrideLzh GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
9-循环程序设计
pridelizihao一、循环程序设计
(1)循环程序设计示例
- 两种循环结构
- 简单循环示例
- 简单循环程序
1 | //统计无符号整数n作为十进制数时的位数 |
- 反汇编之后
1 | //堆栈传参数,eax传返回值 |
简单分析
- 堆栈示例
- 32位数除法,先把被除数扩展到64位,这里是无符号数,所以直接0扩展就行。使用64位无符号数除法
div OPDR
,被除数放在edx:eax
中,除数OPDR这里是ecx,商存在eax
,余数在edx
- 没优化,改一个数的值要三步:从堆栈取到寄存器,改寄存器值,存回堆栈。几乎所有数据计算之后都要先在堆栈更新,要用时再从堆栈取
- 堆栈示例
反汇编之后
1 | push ebp |
- 简单分析
- 堆栈分析
- 仍使用64位无符号数除法
div OPDR
,但除数OPDR用了源变址寄存器esi
,可能因为本质是指针寄存器,所以这里利用堆栈给它赋值,esi=0x0A - 优化
- 每轮循环一开始就把n取到寄存器,循环结束时才存回,减少堆栈操作。
- 使用test指令判断等于0
- 堆栈分析
(2)循环指令
循环指令的说明
- 类似于条件转移指令,段内转移,相对转移方式。
- 通过在指令指针寄存器EIP上加一个地址差的方式实现转移。
- 只用一个字节(8位)表示地址差,转移范围仅在-128至+127之间。
- 在保护方式(32位代码段)下,以ECX作为循环计数器。在实方式下,以CX作为循环计数器。
- 不影响各标志。
计数循环指令LOOP
名称 | LOOP(计数循环指令) |
---|---|
格式 | LOOP LABEL |
动作 | 令使寄存器ECX的值减1,如果结果不等于0,则转移到标号LABEL处,否则顺序执行LOOP指令后的指令 |
注意 | 用于循环次数已知的循环,如for循环 |
计数器必须用ecx先设置计数器ECX初值,即循环次数。 | |
由于首先进行ECX减1操作,再判结果是否为0,所以最多可循环 2 32 2^{32} 232遍。 |
1 | ;统计寄存器EAX中位是1的个数 |
- **等于/全零循环指令LOOPE/LOOPZ
名称 | LOOPE/LOOPZ(等于/全零循环指令) |
---|---|
格式 | LOOPE(LOOPZ) LABEL |
动作 | 指令使寄存器ECX的值减1,如果结果不等于0,并且零标志ZF等于1(表示相等),则转移到标号LABEL处,否则顺序执行。 |
注意 | 适用于循环比较直到找到相等字符的情况 |
同一条指令,有两个助记符 | |
指令本身实施的ECX减1操作不影响标志 | |
可以在循环开始前把ecx设为-1,相当于最大循环FFFFFFFFH-1次,退出循环后用not ecx 把ecx按位取反,即可得LOOPE执行次数 |
1 | //在一个字符数组中查找第一个非空格字符,假设字符数组buff的长度为100: |
- 不等于/非零循环指令LOOPNE/LOOPNZ
名称 | LOOPNE/LOOPNZ(等于/全零循环指令) |
---|---|
格式 | LOOPNE(LOOPNZ) LABEL |
动作 | 指令使寄存器ECX的值减1,如果结果不等于0,并且零标志ZF等于0(表示不相等),则转移到标号LABEL处,否则顺序执行。 |
注意 | 适用于循环比较直到找到不相等字符的情况 |
同一条指令,有两个助记符 | |
指令本身实施的ECX减1操作不影响标志 | |
可以在循环开始前把ecx设为-1,相当于最大循环FFFFFFFFH-1次,退出循环后用not ecx 把ecx按位取反,即可得LOOPNE执行次数 |
1 | //演示LOOPNE指令的使用:嵌入汇编代码形式,测量由用户输入的字符串之长度 |
(3)计数器转移指令
- 上面的第一条
LOOP
指令,提供了一种指定循环次数的方法,但它有一个问题:由于是先将ecx减一再判断,当设定循环次数为0时,实际上会循环FFFFFFFFH次。为了解决这个问题,IA32专门提供了一条用ECX是否为0作为判断条件的条件转移指令JECXZ/JCXZ
名称 | JECXZ/JCXZ(计数器转移指令) |
---|---|
格式 | JECXZ(JCXZ) LABEL |
动作 | 指令实现当寄存器ECX(CX)的值等于0时转移到标号LABEL处,否则顺序执行。 |
注意 | 通常在上面几条循环指令之前使用,这样当循环次数为0时,就可以跳过循环体 |
JECXZ对应判断ECX值;JCXZ对应判断CX值 |
1 | //计算由用户输入的若干成绩的平均值 |
说明:
堆栈示意
32位有符号数除法,先用
CDQ
把EAX
符号扩展到EDX:EAX
二、综合示例
把二进制数转换为十进制数的ASCII码串
方法:
- 把一个整数除以10,所得的余数就是个位数。
- 把所得的商再除以10,所得的余数就是十位数。
- 继续把所得的商除以10,所得的余数就是百位数。
- 依次类推,就可以得到一个整数的各位十进制数字了。
32位二进制数能表示的最大十进制数只有10位,循环地除上10次,就可以得到各位十进制数,注意这样得到的结果最前面有若干个0
把一位十进制数转换为对应的ASCII码,只要加上数字符‘0’的ASCII码。
存放顺序:
由于先得到个位数,然后得到十位数,再得到百位数,所以在把所得的各位十进制数的ASCII码存放到字符串中去时,要从字符串的尾部开始。
1 | int main( ) |
- 改进上面的程序
(1)设二进制数是有符号的。如果负数,则所得字符串的第一个字符应该是负号。
(2)不需要前端可能出现的字符‘0’
1 | int main( ) |
评论
匿名评论隐私政策
TwikooValine
✅ 你无需删除空行,直接评论以获取最佳展示效果