正如我早先说的那样,当问题归结于减小代码的大小的时候,你最好让编译器为你做这件事。然而,如果处理后的程序代码对于你可得的只读存贮器仍然太大了,还有几种技术你可以用来进一步减少体程序的大小。在本节中,自动的和人工的代码优化我们都要讨论。
当然,墨菲法则指出,第一次你启用编译器的优化特性后,你先前的工作程序会突然失效,也许自动优化最臭名昭著的是“死码删除”。这种优化会删除那些编译器相信是多余的或者是不相关的代码,比如,把零和一个变量相加不需要任何的计算时间。但是你可能还是希望如果程序代码执行了tb编译器不了解的函数,编译器能够产生那些“不相关”的指示。
比如,下面这段给出的代码,大部分优化编译器会去除第一条语句,因为*pControl 在重写(第三行)之前没有使用过:
*pControl = DISABLE;
*pData = 'a';
*pCotrol = ENABLE;
但是如果 pControl 和pData 实际上是指向内存映像设备寄存器的指针怎么办?这种情况下,外设在这个字节的数据写入之前将接收不到DISABLE 的命令。这可能会潜在地毁坏处理器和这个外设之间的所有未来的交互作用。为了使你避免这种问题,你必须用关键字“volatile”声明所有指向内存映像设备寄存器的指针和线程之间(或者是一个线程和一个中断服务程序之间)共享的全局变量。你只要漏掉了它们中的一个,墨菲法则就会在你的工程的最后几天里回来,搅得你心神不宁。我保证。
——————————————————————————————————
警告:千万不要误以为程序优化后的行为会和未优化时的一样。你必须在每一次新的优化后完全重新测试你的软件,以确保它的行为没有发生改变。
——————————————————————————————————
更糟糕的是,或者退一步说,调试一个优化过的程序是富有挑战性的。启用了编译器的优化后,在源代码中的一行和实现这行代码的那组处理器指令之间的关联关系变得更加微弱了。那些特定的指令可能被移动或者拆分开来,或者两个类似的代码可能现在共用一个共同的实现。实际上,tb高级语言程序的有些行可能完全从程序中去除了(正如在前面例子里那样)。结果,你可能无法在程序特定的一行上设置一个断点或者无法研究一个感兴趣变量的值。
一旦你使用了自动优化,这里有一些关于用手工的办法进一步减少代码大小的技巧。
避免使用标准库例程
为了减少你的程序的大小,你所能做的最好的一件事情就是避免使用大的标准库例程。很多最大的库例程代价昂贵,只是因为它们设法处理所有可能的情况。你自己有可能用更少的代码实现一个子功能。比如,标准C 的库中的spintf例程是出了名的大。这个庞大代码中有相当一部分是位于它所依赖的浮点数处理例程。但是如果你不需要格式化显示浮点数值(%f 或者%d),那么你可以写你自己的sprintf 的整数专用版本,并且可以节省几千字节的代码空间。实际上,一些标准C 的库(这让我想起Cygnus 的newlib)里恰好包含了这样一个函数,叫作sprintf。
本地字长
每一个处理器都有一个本地字长,并且ANSI C 和C++标准规定数据类型int必须总是对应到那个字长。处理更小或者更大的数据类型有时需要使用附加的机器语言指令。在你的程序中通过尽可能的一致使用int 类型,你也许能够从你的程序中削减宝贵的几百字节。
goto 语句
就像对待全局变量一样,好的软件工程实践规定反对使用这项技术。但是危急的时候,goto 语句可以用来去除复杂的控制结构或者共享一块经常重复的代码。
除了这些技术以外,在前一部分介绍的几种方法可能也会有帮助,特别是查询表、手工编写汇编、寄存器变最以及全局变量。在这些技术之中,利用手工编写汇编通常可以得到代码最大幅度的减少量。