当前课程知识点:嵌入式软件设计 > 第三章 ARC C程序优化 > 3.2 变量的数据类型选择 > 3.2 变量的数据类型选择
各位同学大家好
我们来学习变量的数据类型选择
假设我们现在采用的是
32位的ARM处理器
Load Store不同数据类型的时候
它的效率是不同的
我们通过一个例子来进行说明
这个例子是求校验和
也就是现在有一段内存空间
我们要把
其中每一个单元的数全加起来
然后拿到这个和
由这个和来判断
这段空间的数值存储有没有错误
我们来看一下这段代码
它有一个整型指针的参数
叫做data
然后定义了一个字符型变量i
用这个i作为将来的循环变量
sun是最终要返回的校验和
通过一个64次的循环
把每一个单元的值都加到sum上
这就是当前这个函数的基本功能
然后我们要做一些讨论
把i声明为字符类型的优缺点
字符类型的表示范围超过了64
因为它可以达到255
所以对于这个程序来说完全够用
如果我们采用了字符类型
由于它只有八位
是否能够
更节省寄存器和内存空间呢
这是我们需要考虑的问题
怎么办呢
我们把刚才的程序反汇编成汇编指令
然后看一下
它实际都做了哪些工作
这就是它对应的汇编代码
第一行是MOV r2, r0
r0就是C语言当中的
函数的第一个参数
也就是data指针
指向这段空间的起始地址
我们先把起始地址赋值给r2
因为r0将来另有他用
接下来看第二行
我们给r0赋值为0
就是r0我们将来要用来保存和
所以首先要给它清零
r1是什么呢
r1是计数器的值
也就是当前访问到了哪一个元素
然后我们给它逻辑左移两位
其目的是什么呢
因为ARM是一个32位处理器
每一个字是四个字节
我们给它逻辑左移两位
恰好相当于是给它乘4
然后把它和r2相加
r2就是起始地址了
这个加的和就是
每次我们要找的元素的起始地址
然后我们把地址处的内容拿给r3
在这个循环的第一轮中
r3是整个内存区域第一个单元的值
那么这是一个32位的数据
接下来我们给r1自加
也就是它加1
相当于循环变量自加
再接下来注意这条语句
我们要把r1和0xff做一个与操作
结果还是放到r1中
那么大家想这个有什么用
由于r1是用来保存循环变量
而我们把循环变量i
设计为了一个字符类型的值
那么它的值最多不能够超过255
所以在汇编程序中
就要对此做出保证
把它和0xff做一个与操作
就能够让它的值不会超过255
再下面把r1和0x40做比较
0x40的对应的十进制数就是64
这个是做一个循环终止条件的判断
然后再下面是一个加法
把r0和r3相加结果放到r0中
也就是把刚取出来的r3的值
加到和当中
和就保存在r0里面
然后再下面就要做跳转
根据刚才比较的结果
如果r1的值小于0x40
它就会跳到loop这个地方
进行下一轮循环
否则就往下走
往下走就是把r14的值送给pc
r14就是链接寄存器
它保存的是程序的返回地址
所以把它的值赋值给pc
那么这个函数就会返回
这个就是校验和对应的汇编代码
那么在其中我们要重点讨论的就是
刚才红色的那条指令
i是一个字符型的变量
超过255就必须给它归零
所以汇编程序中要确保不超过255
刚才那个程序一共是十条汇编指令
我们可以对这个程序做一点修改
比如说我们把这个循环变量i
用一个无符号整型变量来代替
我们看一下效果会怎么样
它的汇编指令一共是九条指令
比刚才少了一条
少了哪一条呢
恰好就是对i做约束的那条指令
也就是把r1的值和0xFF相与
那么这条指令就没有了
因为现在循环变量是一个整型变量
我们不需要保证它总是小于255
所以那条与操作的指令就没有了
这样的话程序就少了一条指令
所以它的效率会有一定程度的提高
我们再来看一个例子
这个是16位数据的校验和的例子
我们看这个函数
它的返回值是一个短整型数据
也就是16位的数据
然后它的参数是一个短整型的指针
它的校验和
也是一个短整型的数据类型
在这个汇编代码当中
首先仍然是做初始地址赋值给r2
然后r0用来保存和
r1仍然是循环计数器
但是现在有这样两条红色的指令
一条是把r1逻辑左移一位
然后和r2相加结果送给r3
这个指的是
因为现在是16位数据相加
所以r1每一个值要逻辑左移一位
也就是让它乘以二
那么对应的来说
八位的数据乘以二就是16位的数据
然后和r2这个初始地址
相加结果送给r3
所以在每一圈循环中
r3保存的就是要取的那个数据的地址
然后用这条LDRH指令
把r3这个地址处的
一个16位数据取出来
送给r3这个寄存器
然后接下来是循环变量的增加
以及循环条件的判断
然后这条加法指令
就是要把取出来的r3的值
和保存和的r0的值相加
结果送到r0
再下面我们发现又有两条红色指令
一条是把r0逻辑左移16位
接下来要把r0算术右移16位
为什么要移来移去
因为r0保存的是sum
也就这个和
而我们把这个和定义成了短整型
也就是16位的长度
所以我们在运算过程中
必须随时保证它的长度就是16位
我们给它逻辑左移再算术右移
其实就是把它的高16位去掉了
通过这种方式
保证这个和永远是短整型
然后接下来
就做一个循环终止条件的判断
如果循环没有结束那就往上跳
如果结束了就往下跳
程序就结束了
通过这个例子
我们看到它一共有12条语句
比刚才的九条多了三条
尤其重要的是
在这个循环内部我们发现
反复做移位
实际上循环执行的过程中
每一圈都要增加几条指令
所以它的总的时间会增加很多
那么这种编程方式是不够好的
我们做一个讨论
由于LDRH指令和LDR指令不同
它不支持移位地址偏移
所以需要用一条指令单独计算地址
我们必须这样做
两次移位对应的是短整型数据类型
怎么样克服刚才程序的缺点呢
我们可以用data指针来操作数据
避免使用数组
因为我们可以看到
刚才是使用数组进行操作的
所以我们可以用指针来操作
然后可以运算过程中
都用整型来计算
计算完之后再返回短整型的数据
也就是我们把程序做这样的修改
我们发现校验和定义成了整型
然后在这个循环运算过程中
用的都是整型
只不过返回的时候
我们把返回值
做了一个到短整型的强制类型转换
然后返回
针对这样的代码
我们看一下它对应的汇编语句
首先从内存中取数的这个指令
用一条红色的语句就能够完成了
其次循环的起始位置在这loop
然后循环的截止位置
在BCC这条语句
我们发现
把数据类型从整型
转换成短整型的两条语句
挪到了循环的外面
所以这样总体上来说
程序不但更加短小
而且由于关键语句移到循环的外面
整体上它的执行的时间会少很多
从而获得了程序效率的提升
我们总结一下
通过采用整型类型
省去了多余的移位操作
移位移动到循环外
同时尽量使用整型数据
那什么时候用字符型和短整型呢
如果你要用它的溢出归零的特性
那你就用
否则你尽量就用整型数据
这部分内容我们就介绍到这
谢谢大家
-1.1 嵌入式系统概述
-1.2 嵌入式软件设计概述
-第一章测试 概述
-2.1 软件架构
--2.1 软件架构
-2.2 C的面向对象化
-2.3 汉字处理
--2.3 汉字处理
-2.4 屏幕操作
--2.4 屏幕操作
-2.5 输入事件
--2.5 输入事件
-第二章测试 嵌入式C编程架构
-3.1 优化思路
--3.1 优化思路
-3.2 变量的数据类型选择
-3.3 次数固定的循环优化
-3.4 次数不定的循环优化
-3.5 循环展开
--3.5 循环展开
-3.6 指针别名
--3.6 指针别名
-3.7 结构体
--3.7 结构体
-第三章测试 ARC C程序优化
-4.1 Linux简介
-4.2 Linux的shell
-4.3 Linux的基本操作
-4.4 Linux的网络命令
-第四章测试 Linux操作系统
-5.1 工具链概述
-5.2 编辑器vi
-5.3 gcc简介
-5.4 gcc的使用
-5.5 gdb简介
-第五章-1测试 Linux C编程工具链
-5.6 Makefile工作原理
-5.7 Makefile实例分析
-5.8 Makefile设计
-5.9 Makefile综合实例
-第五章-2测试 Makefile
-6.1 基于Linux的嵌入式平台
-6.2 BootLoader
-6.3 应用程序设计流程
-第六章测试 嵌入式软件平台构建
-7.1 文件的属性
-7.2 文件操作
--7.2 文件操作
-7.3 文件操作举例
-7.4 目录操作
--7.4 目录操作
-7.5 获取目录列表
-7.6 内存映像
--7.6 内存映像
-7.7 内存影响举例
-第七章测试 文件目录和内存操作
-8.1 进程创建
--8.1 进程创建
-8.2 在进程中启动程序
-8.3 等待进程结束
-8.4 线程简介
--8.4 线程简介
-8.5 多线程编程举例
-8.6 线程同步
--8.6 线程同步
-8.7 线程的属性
-第八章测试 进程与线程
-9.1 信号简介
--9.1 信号简介
-9.2 发送和捕获信号
-9.3 更健壮的信号接口
-9.4 信号集处理
-第九章测试 信号
-10.1 有名管道
-10.2 无名管道
-10.3 信号量简介
-10.4 信号量举例
-10.5 共享内存简介
-10.6 共享内存举例
-10.7 消息队列简介
-10.8 消息队列举例
-第十章测试 进程间通信
-11.1 套接字简介
-11.2 套接字举例
-11.3 套接字接口函数
-11.4 网络套接字
-11.5 访问系统服务
-11.6 多客户
--11.6 多客户
-第十一章测试 套接字
-12.1 内核模块简介
-12.2 内核模块设计
-12.3 Linux设备驱动程序简介
-12.4 驱动程序的数据结构
-12.5 虚拟字符设备驱动程序实例
-第十二章-1测试 模块与驱动程序
-第十二章-2测试 驱动程序实例
-13.1 STM32简介
-13.2 STM32常用片内资源
-13.3 GPIO编程方法--寄存器方式
-13.4 LED流水灯实例--GPIO寄存器方式
-13.5 GPIO编程方法--标准库方式
-13.6 LED灯闪烁实例--GPIO标准库方式
-第十三章测试 STM32编程方法
-14.1 Proteus简介
-14.2 流水灯仿真实例
-14.3 通用定时器仿真
-14.4 外部中断仿真
-第十四章测试 嵌入式软件仿真

