C源程序优化
- 格式:doc
- 大小:34.50 KB
- 文档页数:8
源程序中代码的优化和ICCAVR常见的错误代码及其解释(1)用移位实现乘除法运算a=a*4;b=b/4;可以改为:a=a<<2;b=b>>2;说明:通常如果需要乘以或除以2n,都可以用移位的方法代替。
在ICCAVR中,如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。
用移位的方法得到代码比调用乘除法子程序生成的代码效率高。
实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:a=a*9可以改为:a=(a<<3)+aICCAVR常见的错误代码及其解释!E Can't open input file C:\Documents错误分析:错误提示的大体意思是:无法打开C盘根目录下的文件“Documents”。
事实上,我们都知道这里的Documents显然是Documents and Setting文件夹名称的前一个单词。
ICC并不支持路径名称中含有空格,因此系统将Documents and Setting的第一个空格认作是分隔符,将and和Setting及其以后的东西都认作是参数。
这样看来,Documents并不是一个存在的文件,自然无法打开,于是我们就看到了这样的错误提示。
解决方法:将文件拷贝到一个路径名中不含空格的文件夹中。
!E (XXXXX): cannot include source file ""; file not found错误分析:在ICC7以下的版本中,系统并不会自动到安装目录下的include文件夹中寻找所需的头文件。
此时,如果我们未能正确的通过Project->Option->Paths选项卡设定正确的路径,就会看到以上的提示。
当然,如果工程中使用的某些头文件路径不正确的话,也会看到这样的提示。
解决方法:如果信息提示缺失的是系统头文件,正确设定Paths选项卡即可;如果信息提示缺失的是用户头文件,我们应该查找头文件的正确位置,并修改程序中的包含信息。
UNIX系统开发-编译过程概述编译过程概述了解一些编译知识的读者都知道,所谓编译,就是在编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。
UNIX环境下的C编译系统所遵循的也是这么一个一般的过程。
值得注意的是这个过程并不是有某个单个程序完成的,而是有多个分别完成某一方面工作的程序组合完成的。
这一设计思想同我们最初提到的UNIX系统软件功能专一的特点是相符的。
归纳起来,可以将UNIX环境下C编译系统的工作过程下图所示。
C源程序头文件-->预编译处理(cpp)-->编译程序本身-->优化程序-->汇编程序-->链接程序-->可执行文件一般我们用cc命令来完成对源程序的编译工作。
此cc命令并不是一个二进制的可执行程序,而是一个shell命令文件。
它的工作就是依次调用我们上面所列出的各个完成某部分工作的具体程序,将指定的c源程序转换成可执行的代码。
1.编译预处理在此阶段,预编译程序读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。
C语言中的伪指令主要包括以下四个方面(1)宏定义指令,如# define Name TokenString,#undef等。
对于前一个伪指令,预编译所要作得的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。
对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。
这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。
预编译程序将根据有关的文件,将那些不必要的代码过滤掉(3)头文件包含指令,如#include "FileName"或者#include <FileName>等。
C 语言实现单片机控制步进电机加减速源程序1. 引言在现代工业控制系统中,步进电机作为一种常见的执行元件,广泛应用于各种自动化设备中。
而作为一种常见的嵌入式软件开发语言,C 语言在单片机控制步进电机的加减速过程中具有重要的作用。
本文将从单片机控制步进电机的加减速原理入手,结合 C 语言的编程技巧,介绍如何实现单片机控制步进电机的加减速源程序。
2. 单片机控制步进电机的加减速原理步进电机是一种能够精确控制角度的电机,它通过控制每个步骤的脉冲数来实现旋转。
在单片机控制步进电机的加减速过程中,需要考虑步进电机的加速阶段、匀速阶段和减速阶段。
在加速阶段,需要逐渐增加脉冲的频率,使步进电机的转速逐渐增加;在匀速阶段,需要保持恒定的脉冲频率,使步进电机以匀速旋转;在减速阶段,需要逐渐减小脉冲的频率,使步进电机的转速逐渐减小。
这一过程需要通过单片机的定时器和输出控制来实现。
3. C 语言实现步进电机加减速的源程序在 C 语言中,可以通过操作单片机的 GPIO 来控制步进电机的旋转。
在编写源程序时,需要使用单片机的定时器模块来生成脉冲信号,以控制步进电机的旋转角度和速度。
以下是一个简单的 C 语言源程序,用于实现步进电机的加减速控制:```c#include <reg52.h>void main() {// 初始化定时器// 设置脉冲频率,控制步进电机的加减速过程// 控制步进电机的方向// 控制步进电机的启停}```4. 总结与回顾通过本文的介绍,我们了解了单片机控制步进电机的加减速原理和 C 语言实现步进电机加减速源程序的基本思路。
掌握这些知识之后,我们可以更灵活地应用在实际的嵌入式系统开发中。
在实际项目中,我们还可以根据具体的步进电机型号和控制要求,进一步优化 C 语言源程序,实现更加精准和稳定的步进电机控制。
希望本文能为读者在单片机控制步进电机方面的学习和应用提供一定的帮助。
5. 个人观点与理解在我看来,掌握 C 语言实现单片机控制步进电机加减速源程序的技术是非常重要的。
数又很多,往往几个时脉就可以完成却浪费时间在存取堆栈的内容上,所以干脆将这些很短的子程序直接写在主程序当中,以减少时脉数。
方法六写汇编语言虽然由C语言所编译出来的汇编语言可以正确无误的执行,但是这个汇编语言却不是最有效率的写法,所以为了增加程序的效率,于是在某些地方,例如一些被呼叫很多次且程序代码不长的函式(function),必须改以自己动手写汇编语言来取代。
方法七利用平行处理的观念C6x是一颗功能强大的处理器,它CPU勺内部提供了八个可以执行不同指令的单元,也就是说最多可以同时处理八个指令。
所以如果我们可以用它来作平行处理,我们就可以大大的缩短程序执行的时间,最有效率的来利用它来作解码的动作。
最后还要知道:第三级优化(-03),效率不高(经验),还有一些诸如用一条读32位的指令读两个相邻的16位数据等,具体情况可以看看C优化手册。
但这些效率都不高(虽然ti的宣传说能达到80%我自己做的时候发现绝对没有这个效率!65泌差不多),如果要提高效率只能用汇编来做了。
还有要看看你的c程序是怎么编的,如果里面有很多中断的话,6000可以说没什么优势。
还有,profiler 的数据也是不准确的,比实际的要大,大多少不好说。
还有dsp在初始化的时候特别慢,这些时间就不要和pc机相比了,如果要比就比核心的部分。
关于profileC6x的Debug工具提供了一个profile 界面。
在图9中,包括了几个重要的窗口,左上角的窗口是显示出我们写的C语言,可以让我们知道现在做到了哪一步。
右上角的窗口显示的是C6x所编译出来的汇编语言,同样的我们也可以知道现在做到了哪一步。
左下角的窗口是命令列,是让我们下指令以及显示讯息的窗口。
而中间的profile 窗口就是在profile模式下最重要的窗口,它显示出的项目如下表:表5:profile 的各项参数[8]字段意义Cou nt被呼叫的次数In elusive 包含子程序的总执行clock数Inel-Max包含子程序的执行一次最大clock数Exclusive不包含子程序的总执行clock数Excl-Max不包含子程序的执行一次最大clock数利用这个profile 模式我们可以用来分析程序中每个函数被呼叫的次数、执行的时脉数等等。
1.嵌入式系统基本含义是什么?为什么说单片机是典型的嵌入式系统?嵌入式系统(Embedded System):面向测控对象,嵌入到实际应用系统中,实现嵌入式应用的计算机称之为嵌入式计算机系统,简称嵌入式系统(Embedded System)。
嵌入式系统是面向测控对象,嵌入到实际应用系统中的计算机系统的统称。
嵌入式系统通常有4 种:①工控机;②通用CPU 模块;③嵌入式微处理器(Embedded Processor);嵌入式微控制器(Embedded Microcontroller)。
前两者是基于通用计算机系统,即将通用计算机系统用于测控对象。
后两者是基于芯片形态的计算机系统,其中嵌入式MCU 是在通用CPU 基础上发展,增加满足测控对象要求的外围接口电路,用于测控领域。
而嵌入式MCU则是在嵌入式系统的概念广泛使用后,给传统MCU 定位的称呼。
所以,可以说MCU 是最典型的、最广泛的嵌入式系统。
3.比较MCU与CPU的区别与联系。
从总体上说,通用计算机系统主要用于数值计算、信息处理,兼顾控制功能,而嵌入式计算机系统主要用于控制领域,兼顾数据处理。
与单纯的高速海量计算要求不同,通信、测控、数据传输等领域主要表现在:①直接面向控制对象;②嵌入到具体的应用体中,而不以计算机的面貌出现;③能在现场可靠地运行;④体积小,应用灵活;⑤突出控制功能,特别是对外部信息的捕捉与丰富的输入输出(I/O)功能等。
由此可以看出,满足这些要求的计算机与满足高速数值计算的计算机是不可兼得的就MCU 组成而言,虽然它只是一块芯片,但包含了计算机的基本组成单元,仍由运算器、控制器、存储器、输入设备、输出设备五部分组成,只不过这些都集成在一块芯片上,这种结构使得MCU成为具有独特功能的计算机。
2.简述MCU的基本组成及应用领域,简述嵌入式系统的特点。
MCU的基本组成:由运算器、控制器、存储器、输入设备、输出设备五部分组成。
MCU应用领域:通信、测控、数据传输等领域。
gcc优化选项-O1-O2-O3-Os优先级少优化->多优化:O0 -->> O1 -->> O2 -->> O3-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼英⽂解析:`-O '`-O1 'Optimize. Optimizing compilation takes somewhat more time, and alot more memory for a large function.With `-O ', the compiler tries to reduce code size and executiontime, without performing any optimizations that take a great dealof compilation time.`-O ' turns on the following optimization flags:-fdefer-pop-fdelayed-branch-fguess-branch-probability-fcprop-registers-floop-optimize-fif-conversion-fif-conversion2-ftree-ccp-ftree-dce-ftree-dominator-opts-ftree-dse-ftree-ter-ftree-lrs-ftree-sra-ftree-copyrename-ftree-fre-ftree-ch-funit-at-a-time-fmerge-constants`-O ' also turns on `-fomit-frame-pointer ' on machines where doingso does not interfere with debugging.`-O ' doesn 't turn on `-ftree-sra ' for the Ada compiler. Thisoption must be explicitly specified on the command line to beenabled for the Ada compiler.`-O2 'Optimize even more. GCC performs nearly all supportedoptimizations that do not involve a space-speed tradeoff. Thecompiler does not perform loop unrolling or function inlining whenyou specify `-O2 '. As compared to `-O ', this option increasesboth compilation time and the performance of the generated code.`-O2 ' turns on all optimization flags specified by `-O '. It alsoturns on the following optimization flags:-fthread-jumps-fcrossjumping-foptimize-sibling-calls-fcse-follow-jumps -fcse-skip-blocks-fgcse -fgcse-lm-fexpensive-optimizations-fstrength-reduce-frerun-cse-after-loop -frerun-loop-opt-fcaller-saves-fpeephole2-fschedule-insns -fschedule-insns2-fsched-interblock -fsched-spec-fregmove-fstrict-aliasing-fdelete-null-pointer-checks-freorder-blocks -freorder-functions-falign-functions -falign-jumps-falign-loops -falign-labels-ftree-vrp-ftree-prePlease note the warning under `-fgcse ' about invoking `-O2 ' onprograms that use computed gotos.`-O3 'Optimize yet more. `-O3 ' turns on all optimizations specified by`-O2 ' and also turns on the `-finline-functions ',`-funswitch-loops ' and `-fgcse-after-reload ' options.`-O0 'Do not optimize. This is the default.///==================另外还有个Os选项==========================原来-Os相当于-O2.5。
C代码在DM642中的优化祁艳杰;杨泽辉【摘要】C compiler in DM642 can compile and convert C language program into C6000 DSP assemble language automatically. Therefore) the developers can realize preliminary design of DSP software by using the advanced language directly and the cycle of the development can be shortened. Since the C language program for DSP is a real-time process for few data points, the C code must be optimized to meet the real-time requirement. The optimization process of the C code in DM642 is described with an example of JPEG in TMS320DM642.%DM642中的C编译器可以将C语言程序自动编译转化为C6000 DSP汇编程序,这样开发者就可以直接利用高级语言实现DSP软件的初步设计,缩短开发周期.但面向DSP的C语言程序是针对极少数据点的实时处理过程,因而需要对C代码进行优化,以满足实时性.在此以一个图像压缩算法JPEG在TMS320DM642中的实现为例,简要说明C代码在DM642中的优化过程.【期刊名称】《现代电子技术》【年(卷),期】2012(035)002【总页数】3页(P21-23)【关键词】C语言程序;DM642;C编译器;代码优化【作者】祁艳杰;杨泽辉【作者单位】太原科技大学电子工程学院,山西太原030024;山西财政税务专科学校,山西太原030024【正文语种】中文【中图分类】TN919-340 引言自第一个数字信号处理器问世以来,DSP就凭着其特有的灵活性、稳定性、功耗小等特点,给数字信号处理带来了巨大的发展机遇,应用领域拓展到国民经济生活的诸多方面,且随着DSP运算速度的提高,能够实时处理信号的能力大大增加,数字信号处理的研究重点也转向了高速实时处理。
C语⾔对源程序处理的四个步骤:预处理、编译、汇编、链接——预处理篇预处理1)预处理的基本概念C语⾔对源程序处理的四个步骤:预处理、编译、汇编、链接。
预处理是在程序源代码被编译之前,由预处理器(Preprocessor)对程序源代码进⾏的处理。
这个过程并不对程序的源代码语法进⾏解析,但它会把源代码分割或处理成为特定的符号为下⼀步的编译做准备⼯作。
2)预编译命令C编译器提供的预处理功能主要有以下四种:1)⽂件包含 #include2)宏定义 #define3)条件编译 #if #endif ..4)⼀些特殊作⽤的预定义宏a、⽂件包含处理1)⽂件包含处理⽂件包含处理”是指⼀个源⽂件可以将另外⼀个⽂件的全部内容包含进来。
C语⾔提供了#include命令⽤来实现“⽂件包含”的操作。
2)#include< > 与 #include ""的区别" "表⽰系统先在file1.c所在的当前⽬录找file1.h,如果找不到,再按系统指定的⽬录检索。
< >表⽰系统直接按系统指定的⽬录检索。
注意:1. #include <>常⽤于包含库函数的头⽂件2. #include " "常⽤于包含⾃定义的头⽂件 (⾃定义的头⽂件常⽤“ ”,因为使⽤< >时需要在系统⽬录检索中加⼊⾃定义头⽂件的绝对地址/相对地址否则⽆法检索到该⾃定义的头⽂件,编译时会报错)3. 理论上#include可以包含任意格式的⽂件(.c .h等) ,但我们⼀般⽤于头⽂件的包含。
b、宏定义1)基本概念在源程序中,允许⼀个标识符(宏名)来表⽰⼀个语⾔符号字符串⽤指定的符号代替指定的信息。
在C语⾔中,“宏”分为:⽆参数的宏和有参数的宏。
2)⽆参数的宏定义#define 宏名 字符串例: #define PI 3.141926在编译预处理时,将程序中在该语句以后出现的所有的PI都⽤3.1415926代替。
编译原理的名词解释编译原理是计算机科学中的一门重要课程,它研究的是如何将高级语言程序转化为计算机能够执行的机器指令。
编译原理涉及许多专业术语和概念,下面将对其中一些重要的名词进行解释。
词法分析(Lexical Analysis)词法分析是编译过程中的第一个阶段,也被称为扫描器。
它负责将源程序中的字符序列转化为单词(词法单元)的序列。
在词法分析的过程中,会忽略不需要关注的字符,如空格和注释。
语法分析(Syntax Analysis)语法分析是编译过程中的第二个阶段,也被称为解析器。
它负责根据词法分析阶段产生的词法单元序列,构建出一棵语法树。
通过语法分析,可以检查源程序是否符合语法规范,并将程序转化为抽象语法树。
语义分析(Semantic Analysis)语义分析是编译过程中的第三个阶段,它负责对语法树进行语义检查和语义规则的应用。
语义分析可以捕捉到一些错误,在编译过程中对源程序进行修正。
此外,语义分析还对程序中的语义逻辑进行处理,包括类型检查、作用域检查等。
中间代码生成(Intermediate Code Generation)中间代码是一种介于高级语言和目标机器语言之间的中间形式。
中间代码生成是编译过程中的一个重要阶段,它将源程序翻译为一种中间表示形式。
中间代码的生成可以便于程序的优化和后续阶段的处理。
代码优化(Code Optimization)代码优化是编译过程中的一个关键环节,它旨在改进生成的目标代码的效率和质量。
代码优化技术包括常量传播、死代码消除、循环优化等。
通过代码优化,可以提高程序的执行效率和资源利用率,改善程序的性能。
目标代码生成(Code Generation)目标代码生成是编译过程中的最后一个阶段,它将中间代码转化为目标机器的机器指令。
目标代码生成需要考虑目标机器的硬件特性和指令集,将中间代码转化为可以被计算机直接执行的机器指令。
符号表(Symbol Table)符号表是编译器中非常重要的数据结构,用于存储程序中出现的所有标识符的信息。
gcc编译c文件并运行的方法GCC是一款广泛使用的编译器,可用于编译多种编程语言,包括C语言。
它是一个开源软件,可以在多种平台上使用,如Linux、Windows和Mac OS等。
GCC编译器可以将C语言程序源代码编译为计算机可以执行的机器代码,这些机器代码可以在计算机上直接运行。
本文将介绍如何使用GCC编译C语言程序,并在计算机上运行它们。
步骤1:安装GCC编译器要使用GCC编译C语言程序,首先需要安装GCC编译器。
在Linux系统上,可以使用以下命令来安装GCC:```sudo apt-get install build-essential```在Windows系统上,可以下载MinGW安装包进行安装。
MinGW(Minimalist GNU for Windows)是一套在Windows上使用GCC编译器的工具集。
在Mac OS系统上,可以使用Homebrew包管理器来安装GCC编译器。
在终端上输入以下命令:```brew install gcc```步骤2:编写C语言程序要编译和运行C语言程序,首先需要编写一个C源代码文件。
C源代码文件通常以“.c”为扩展名。
可以编写一个简单的“hello world”程序,并将其保存为“hello.c”文件。
下面是一个示例程序:```#include <stdio.h>printf("Hello, World!");return 0;}```步骤3:使用GCC编译器编译C语言程序一旦编写好了C语言程序,就可以使用GCC编译器将其编译为可执行程序。
在终端上,进入C语言程序所在目录,并使用以下命令编译程序:```gcc -o hello hello.c```在这个命令中,“-o”参数指定编译器编译完后生成的可执行文件的名称。
在本例中,编译器将生成一个名为“hello”的可执行文件。
步骤4:运行已编译的C语言程序执行上述编译命令后,GCC编译器将生成一个可执行文件。
c语言源程序经过编译程序编译后所产生的文件扩展名在撰写这篇文章之前,让我们首先来深入探讨一下C语言源程序经过编译程序编译后所产生的文件扩展名。
C语言作为一种高级程序设计语言,其源代码需要通过编译器进行编译后才能生成可执行文件。
而在编译过程中,会产生一些特定的文件扩展名,我们将从简到繁地来讨论这个主题。
1. .c源文件我们需要了解的是C语言源程序文件的扩展名为.c。
在编写C语言程序时,我们通常将源代码保存为以.c为后缀的文件,比如hello.c、main.c等。
这些.c文件包含了完整的C语言代码,需要通过编译器将其转换为机器语言才能执行。
2. .obj目标文件在进行编译时,编译器会将.c文件编译生成目标文件,其文件扩展名通常为.obj。
目标文件是编译器输出的中间文件,包含了编译后的机器语言代码和一些符号表等信息,但还不能直接作为可执行文件运行。
3. .exe可执行文件经过连接器的处理,将目标文件连接生成可执行文件,其文件扩展名为.exe。
可执行文件包含了机器语言代码和连接器生成的一些其他信息,可以直接在操作系统中执行,完成程序的运行。
4. .o目标文件和.a库文件在Unix/Linux系统中,编译生成的目标文件通常以.o为扩展名,而库文件通常以.a为扩展名。
这与Windows系统的.obj和.exe略有不同,但本质相同,都是编译和连接生成的文件。
5. 综述C语言源程序经过编译程序编译后所产生的文件扩展名包括.c、.obj (.o)、.exe以及.a等。
这些文件扩展名代表了C语言程序经过编译、连接等阶段生成的不同类型文件,对于我们理解程序编译过程及调试程序都有着重要的意义。
6. 个人观点和理解个人认为,了解C语言源程序编译后所产生的文件扩展名是非常重要的,这有助于我们深入理解程序编译连接的过程,也有助于我们更好地进行程序调试和优化。
对于想要深入学习C语言的初学者来说,掌握这些知识也是基础中的基础。
通过本文的讨论,希望读者能对C语言源程序编译后所产生的文件扩展名有更深入的理解,从而在编程学习和实践中能够更加得心应手。
基于C语言的黄金分割法的优化设计1,黄金分割法的程序流程图2,对应流程图的C语言程序下面应用C语言程序利用黄金分割法求一元函数F=1.2*X^4-1*X^3-9*X^2-22*X+210的最优解,已知初始区间为[1,7] ,取收敛精度e=10-6。
C语言程序如下://机械优化设计,黄金分割法(0.618法)的C语言源程序#include<stdio.h>#include<math.h>#define f(x) 1.2*pow(x,4)+(-1)*pow(x,3)+(-9)*pow(x,2)+(-22)*pow(x,1)+210//定义函数F=1.2*X^4-1*X^3-9*X^2-22*X+210#define M 0.618//定义常变量M=0.618,流程图中的λ用大写字母M代替void main(){double y1,y2,x1,x2,x,a,b,e;//流程图中的а用x代替int n;n=1;printf("请输入收敛精度e="); //流程图中的收敛精度ε用小写字母e代替scanf("%lf",&e);printf("请输入区间左值a=");scanf("%lf",&a);printf("请输入区间右值b=");scanf("%lf",&b);printf("n a b x1 x2 y1 y2\n");x1=b-M*(b-a);x2=a+M*(b-a);y1=f(x1);y2=f(x2);printf("%d %.4lf %.4lf %.4lf %.4lf %.4lf %.4lf\n",n,a,b,x1,x2,y1,y2);n=n++;do{if(y1<y2){b=x2;x2=x1;y2=y1;x1=b-M*(b-a);y1=f(x1);printf("%d %.4lf %.4lf %.4lf %.4lf %.4lf %.4lf\n",n,a,b,x1,x2,y1,y2);n=n++;}else{a=x1;x1=x2;y1=y2;x2=a+M*(b-a);y2=f(x2);printf("%d %.4lf %.4lf %.4lf %.4lf %.4lf %.4lf\n",n,a,b,x1,x2,y1,y2);n=n++;}}while(fabs((b-a)/b)>=e&&fabs((y2-y1)/y2)>=e);x=(a+b)*0.5;printf("x=%.5lf\n",x);getchar();}3.运行结果:。
C源程序优化AVR单片机C语言开发入门指导》一书的第五章第1小节对程序进行优化,通常是指优化程序代码或程序执行速度。
优化代码和优化速度实际上是一个予盾的统一,一般是优化了代码的尺寸,就会带来执行时间的增加,如果优化了程序的执行速度,通常会带来代码增加的副作用,很难鱼与熊掌兼得,只能在设计时掌握一个平衡点。
5.1.1 程序结构的优化1、程序的书写结构虽然书写格式并不会影响生成的代码质量,但是在实际编写程序时还是应该尊循一定的书写规则,一个书写清晰、明了的程序,有利于以后的维护。
在书写程序时,特别是对于While、for、do…while、if…elst、switch…case 等语句或这些语句嵌套组合时,应采用“缩格”的书写形式,2、标识符程序中使用的用户标识符除要遵循标识符的命名规则以外,一般不要用代数符号(如a、b、x1、y1)作为变量名,应选取具有相关含义的英文单词(或缩写)或汉语拼音作为标识符,以增加程序的可读性,如:count、number1、red、work等。
3、程序结构C语言是一种高级程序设计语言,提供了十分完备的规范化流程控制结构。
因此在采用C语言设计单片机应用系统程序时,首先要注意尽可能采用结构化的程序设计方法,这样可使整个应用系统程序结构清晰,便于调试和维护。
于一个较大的应用程序,通常将整个程序按功能分成若干个模块,不同模块完成不同的功能。
各个模块可以分别编写,甚至还可以由不同的程序员编写,一般单个模块完成的功能较为简单,设计和调试也相对容易一些。
在C语言中,一个函数就可以认为是一个模块。
所谓程序模块化,不仅是要将整个程序划分成若干个功能模块,更重要的是,还应该注意保持各个模块之间变量的相对独立性,即保持模块的独立性,尽量少使用全局变量等。
对于一些常用的功能模块,还可以封装为一个应用程序库,以便需要时可以直接调用。
但是在使用模块化时,如果将模块分成太细太小,又会导致程序的执行效率变低(进入和退出一个函数时保护和恢复寄存器占用了一些时间)。
4、定义常数在程序化设计过程中,对于经常使用的一些常数,如果将它直接写到程序中去,一旦常数的数值发生变化,就必须逐个找出程序中所有的常数,并逐一进行修改,这样必然会降低程序的可维护性。
因此,应尽量当采用预处理命令方式来定义常数,而且还可以避免输入错误。
5、使用条件编译能够使用条件编译(ifdef)的地方就使用条件编译而不使用if语句,有利于减少编译生成的代码的长度。
6、表达式对于一个表达式中各种运算执行的优先顺序不太明确或容易混淆的地方,应当采用圆括号明确指定它们的优先顺序。
一个表达式通常不能写得太复杂,如果表达式太复杂,时间久了以后,自己也不容易看得懂,不利于以后的维护。
7、函数对于程序中的函数,在使用之前,应对函数的类型进行说明,对函数类型的说明必须保证它与原来定义的函数类型一致,对于没有参数和没有返回值类型的函数应加上“void”说明。
如果果需要缩短代码的长度,可以将程序中一些公共的程序段定义为函数,在Keil中的高级别优化就是这样的。
如果需要缩短程序的执行时间,在程序调试结束后,将部分函数用宏定义来代替。
注意,应该在程序调试结束后再定义宏,因为大多数编译系统在宏展开之后才会报错,这样会增加排错的难度。
8、尽量少用全局变量,多用局部变量。
因为全局变量是放在数据存储器中,定义一个全局变量,MCU就少一个可以利用的数据存储器空间,如果定义了太多的全局变量,会导致编译器无足够的内存可以分配。
而局部变量大多定位于MCU内部的寄存器中,在绝大多数MCU中,使用寄存器操作速度比数据存储器快,指令也更多更灵活,有利于生成质量更高的代码,而且局部变量所的占用的寄存器和数据存储器在不同的模块中可以重复利用。
9、设定合适的编译程序选项许多编译程序有几种不同的优化选项,在使用前应理解各优化选项的含义,然后选用最合适的一种优化方式。
通常情况下一旦选用最高级优化,编译程序会近乎病态地追求代码优化,可能会影响程序的正确性,导致程序运行出错。
因此应熟悉所使用的编译器,应知道哪些参数在优化时会受到影响,哪些参数不会受到影响。
在ICCAVR中,有“Default”和“Enable Code Compression”两个优化选项。
在CodeVisionAVR中,“Tiny”和“small”两种内存模式。
在IAR中,共有7种不同的内存模式选项。
在GCCAVR中优化选项更多,一不小心更容易选到不恰当的选项。
5.1.2 代码的优化1、选择合适的算法和数据结构应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。
将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。
.选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。
数组与指针具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。
对于大部分的C编译器,使用指针比使用数组生成的代码更短,执行效率更高。
但是在Keil中则相反,使用数组比使用的指针生成的代码更短。
2、使用尽量小的数据类型能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int),特别是能不用浮点型(float)变量就不要使用浮点型变量,使用浮点型变量会使程序代码增加很大。
当然,在定义变量后不能超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。
在keil中,应说明指针所指向的对象类型,少用通用型指针。
在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和%s格式说明符),少用长整型参数(%ld、%lu、%lx 和%lX格式说明符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。
在其它条件不变的情况下,使用%f参数,会使生成的代码的数量增加很多,执行速度降低。
3、使用自加、自减指令通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。
在AVR单片适用的ICCAVR、GCCAVR、IAR等C编译器中,以上几种书写方式生成的代码是一样的,都能够生成inc和dec之类高质量的代码。
4、减少运算的强度可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。
如下:(1)、求余运算。
a=a%8;可以改为:a=a&7;说明:位操作只需一个指令周期即可完成,而大部分的C编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。
通常,只要求是求2n方的余数,均可使用位操作的方法来代替。
(2)、平方运算a=pow(a,2.0);可以改为:a=a*a;说明:在有内置硬件乘法器的单片机中(如51系列),乘法运算比求平方运算快得多,因为浮点数的求平方是通过调用子程序来实现的,在自带硬件乘法器的AVR单片机中,如ATMega163中,乘法运算只需2个时钟周期就可以完成。
就算是在没有内置硬件乘法器的AVR单片机中,乘法运算的子程序比平方运算的子程序代码短,执行速度快。
如果是求3次方,如:a=pow(a,3.0);更改为:a=a*a*a;则效率的改善更明显。
(3)、用移位实现乘除法运算a=a*4;b=b/4;可以改为:a=a<<2;b=b>>2;说明:通常如果需要乘以或除以2n,都可以用移位的方法代替。
在ICCAVR 中,如果乘以2n,都不是调用子程序而是直接生成左移n位的代码,但乘以非2n的整数或除以任何整数,均调用乘除法子程序运算。
用移位的方法得到代码比调用乘除法子程序生成的代码效率高得多。
实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:a=a*9可以改为:a=(a<<3)+a(4)、少用浮点运算int a=200;float b;b=a*89.65在上例中,如果能够不使用浮点运算,而改为长整型,如下:int a=200;long int b;b=a*8965/100;数值大小不变,但是生成的代码却少了非常多。
在很多情况下,如果忽略小数点部分对整个数值的影响不大,就忽略小数点部分,改为整型或长整型。
如果在中间变量为浮点型且不能忽略小数点,也可以将其乘以10n方后转换为长整型数,但在最后运算时应记着除去10n。
6、循环(1)、循环语句对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、指针运算、数组访问等。
应该将没有必要执行多次的操作全部集合在一起,放到一个init的初始化程序中进行。
(2)、延时函数:通常使用的延时函数均采用自加的形式:void delay (void){unsigned int i;for (i=0;i<1000;i++);}将其改为自减延时函数:void delay (void){unsigned int i;for (i=1000;i>0;i--);}两个函数的延时效果相似,但几乎所有的C编译对后一种函数生成的代码均比前一种代码少1~3个字节,因为几乎所有的MCU均有为0转移的指令,采用后一种方式能够生成这类指令。
在使用while循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更少1~3个字母。
但是在循环中有通过循环变量“i”读写数组的指令时,使用预减循环时有可能使数组超界,要引起注意。
(3)while循环和do…while循环用while循环时有以下两种循环形式:unsigned int i;i=0;while (i<1000){i++;//用户程序}或:unsigned int i;i=1000;doi--;//用户程序while (i>0);在这两种循环中,使用do…while循环编译后生成的代码的长度短于while 循环。
7、查表在程序中一般不进行非常复杂的运算,如浮点数的乘除及开方,以及一些复杂的数学模型的插补运算,对这些即消耗时间又消耗资源的运算,应尽量使用查表的方式,并且将数据表置于程序存储区。
如果直接生成所需的表比较困难,也尽量在启动时先计算,然后在数据存储器中生成所需的表,后以在程序运行直接查表就可以了,减少了程序执行过程中重复计算的工作量。
8、其它比如使用在线汇编以及将字符串和一些常量保存在程序存储器中,均能够优化生成的代码。