嵌入式C编程技术(二)
导读:
关键字:
[编者按] 为使广大嵌入式系统应用技术人员系统地了解和掌握一些先进应用、开发技术,本刊从创刊号起开辟《学习园地》栏目。上半年集中介绍嵌入式C编程技术(一)~(六),内容包括单片机C语言应用程序设计中的变量定义和变量空间、C语言编程技巧、函数有效使用及混合编程技术。 嵌入式C编程技术(二) 北京理工大学马忠梅 (4) 静态局部变量 在局部变量说明前加上“static”说明符就构成静态局部变量。它是1种在2次函数调用之间仍能保持其值的局部变量。有些例行程序须要在多次调用之间保存变量的值,自动变量做不到,若用全局变量,有时会带来意外的副作用,这时可采用静态局部变量。在实际应用中,要注意静态变量与自动变量的区别。 下述程序由2个函数组成1个文件,其中main多次调用f,在f()中既有自动变量m,又有静态变量n,从运行结果可以分析它们的区别。 main() { int i,j,k; i=5; j=10; for(k=1;k<=5;k++) f(i,j,k); } f(x,y,z) int x,y,z; { int m; static int n; if(z= =1) { m=0; n=0; } else { m=m+x+y; n=n+x+y; } } 在主函数中,k由1到5,控制循环5次,分别调用函数f(i,j,k)。在被调用函数f(x,y,z)中,第1次z=1,给m和n赋初值,结果m=0,n=0。此后,由于n为静态变量,在2次函数调用之间,其值仍然保存,故以后几次运算,n的值有连续性,每次加15。但m是自动变量,从第2次循环开始,m的初值不再保存,而函数中又未对m重新赋值,故m为1个无意义的不定值。 源程序: int timeint(); int time; int list() { int flag=0; static int count=0; if(++count>=60) flag=timeint(); return(flag); } int timeint() { int temp; static int count=1000; if(!--time){ time=10; count-=60; } temp=time*count; return(temp); } “count”变量在函数的内部定义是静态局部变量,list()函数中定义的“count”变量和timeint()函数中定义的“count”变量在内部RAM中分配不同的地址单元保存它们的值。参见映像文件“.M51”。 映像文件: \ \ \ \ 〖〗PROC〖〗LIST〖〗D:000EH〖〗SYMBOL〖〗flag〖〗D:0008H〖〗SYMBOL〖〗count〖〗\ \ \ \ 〖〗ENDPROC〖〗 LIST〖〗\ \ \ \ 〖〗PROC〖〗TIMEINT〖〗D:0006H〖〗SYMBOL〖〗temp〖〗D:000AH〖〗SYMBOL〖〗count〖〗\ \ \ \ 〖〗ENDPROC〖〗TIMEINT“count”变量在2个函数中分配的内部RAM单元地址分别为08H和0AH。 静态局部变量只在定义它的函数内部有效,任何其他函数都不能访问,甚至在同一编译模块中的函数也是如此。 3 只读变量和变量空间 在程序执行过程中,变量从存储器中读出它的值,然后再把新值写入到存储器中,这就是为何把变量的存储空间定位在RAM中。RAM中的变量是可以修改的,但是,也有一些变量,如错误信息和固定值,程序不须要改变它们。像常规外部变量一样,在RAM空间中保存这些只读变量是浪费RAM空间的。可以使用“#define”定义只读变量为1个常数值,这种方法可以保留RAM空间。下面程序比较了只读值用外部变量说明和用“#define”定义成数值常数。 源程序1: int maxaddr=32767; float pai=3.14159; double list(int addr,int dia) { double length; if(maxaddr<=addr) length=dia*pai; else length=0; return(length); } 列表文件1: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=64\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=64 IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 源程序2: #define maxaddr 32767 #define pai 3.14159 double list(int addr,int dia) { double length; if(maxaddr<=addr) length=dia*pai; else length=0; return(length); } 列表文件2: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=64\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=\ \ \ \ 4 IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 从上面程序结果可以看到只读变量的处理: (1) 定义成外部变量 变量在RAM中保留存储单元,但从不写入。静态占用的RAM单元6个,可覆盖的RAM单元4个。 (2) 定义成数值常数 不在RAM中保留存储单元。静态占用的RAM单元没有,可覆盖的RAM单元4个。 4. 使用自动变量减少变量占用空间 首先看外部变量。外部变量在RAM中分配变量的存储空间。我们使用的许多C程序常常把仅在特定函数使用的变量定义成外部变量。把有限范围使用的变量定义成外部变量无疑会增加变量存储空间的大小。尽可能地把仅在函数内访问的外部变量替换成自动变量来保留变量的存储空间。 当函数被调用时,函数中的自动变量在RAM中保留存储空间;当函数完成后立即释放。在函数中定义自动变量比在模块中定义外部变量可以减少变量的存储空间。当函数嵌套调用时,保留存储空间更为重要。 下面是外部变量和局部变量比较的例程。1个是把仅在1个函数内部访问的变量定义成外部变量,而另一个是定义成局部变量。 源程序1: #define MAXPROC 100 #define ERR 1 int procno; int currproc; int nextproc; int list1() { if((MAXPROC-procno)>=0){ procno++; nextproc=currproc+1; return(nextproc); } else return(ERR); } 映像文件1: D:0008H〖〗PUBLIC〖〗procno〖〗D:000AH〖〗PUBLIC〖〗currproc〖〗D:000CH〖〗PUBLIC〖〗nextproc列表文件1: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=43\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=6\ \ \ \ IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 源程序2: #define MAXPROC 100 #define ERR 1 int procno; int currproc; int list2() { int nextpro; if((MAXPROC-procno)>=0){ procno++; nextpro=currproc+1; return(nextpro); } else return(ERR); } 映像文件2: D:0008H〖〗PUBLIC〖〗procno〖〗D:000AH〖〗PUBLIC〖〗currproc〖〗D:0006H〖〗SYMBOL〖〗nextpro列表文件2: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=38\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=4\ \ \ \ IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. list1()函数把“nextproc”定义成外部变量,在RAM中的两个字节的变量存储单元必须在程序的生命期内一直保留。共占用6个字节的RAM单元。 list2()函数把“nextproc”定义成自动变量。仅在函数执行时访问存储单元,但当函数完成后存储单元立即释放可让其他函数使用,共占用4个字节的RAM单元,因而,可以减少变量使用的存储单元,保留RAM空间。 把只在函数有限范围内使用的变量定义成外部变量浪费RAM和ROM,所以,只在必要的情况下才使用外部变量,可以保留存储空间。 不仅是外部变量,静态变量使用也应尽可能少。在设计程序时要仔细考虑变量的使用范围,避免不必要的空间浪费。 5. 使用位域减少变量占用空间 有时过程控制要使用标志类型的变量。编程者一般对此定义“char”类型变量。当变量用作标志时,它们的值只有“0”或“1”状态,因而,为了标志定义“char”型全局变量是浪费RAM空间。在需要几个做标志用的变量时,使用可位寻址空间某一字节的各位来代替各标志,可以节省RAM空间,这就是使用位域。 源程序1: char flag1; char flag2; char flag3; char flag4; char flag5; char flag6; int list1() { if(flag1) flag2=1; else flag2=0; return(flag2); } 映像文件1: D:0008H〖〗PUBLIC〖〗flag1[]D:0009H〖〗PUBLIC〖〗flag2〖〗D:000AH〖〗PUBLIC〖〗flag3〖〗D:000BH〖〗PUBLIC〖〗flag4〖〗D:000CH〖〗PUBLIC〖〗flag5〖〗D:000DH〖〗PUBLIC〖〗flag6列表文件1: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=20\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=6\ \ \ \ IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 源程序2: char bdata flags; sbit flag1=flags^0; sbit flag2=flags^1; sbit flag3=flags^2; sbit flag4=flags^3; sbit flag5=flags^4; sbit flag6=flags^5; int list2() { if(flag1) flag2=1; else flag2=0; return(flag2); } 映像文件2: D:0020H〖〗PUBLIC〖〗flags〖〗B:0020H.0〖〗PUBLIC〖〗flag1〖〗B:0020H.1〖〗PUBLIC〖〗flag2〖〗B:0020H.2〖〗PUBLIC〖〗flag3〖〗B:0020H.3〖〗PUBLIC〖〗flag4〖〗B:0020H.4〖〗PUBLIC〖〗flag5〖〗B:0020H.5〖〗PUBLIC〖〗flag6列表文件2: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=16\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=1\ \ \ \ IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 上面程序比较了使用6个“char”类型的变量用作标志的函数和另一个使用位域定义位变量用作6个标志的函数。 flag1到flag6用“char”类型定义时,标志占用6个字节的RAM存储单元,当使用位变量时,只需1个字节RAM用作变量存储单元。 6 使用联合减少变量占用空间 联合是允许不同数据类型变量在存储器中分配相同地址的一种定义方式。联合使得访问1个字节的各个位或者访问1个字节所有位成为可能,如上节所示flags和flag1~flag6都是访问同1字节的单元地址。编译器给联合分配空间时,使用联合中最大变量的长度作为联合的长度。联合中的所有成员分享相同的地址。 union datau{ char datacha;/* 1 byte */ intdataint;/* 2 bytes */ double datadou;/* 8 bytes */ }uarea; union array{ int val;/* 2 bytes */ char ary\[100\];/* 100 bytes */ int num\[30\];/* 60 bytes */ }aarea; union tableu{ struct{;/* 22 bytes */ char id; char name\[21\]; }str1; struct{;/* 20 bytes */ char id; char tel\[15\]; int code; int age; }str2; }datatable\[20\]; “uarea”联合,联合要用的空间大小是最大成员“datadou”的8字节。这个地址空间以后可以以“char”,“int”或“double”数据类型访问。 “aarea”联合,联合的最大成员是“ary\[100\]”数组,这样,联合的大小就是100字节。在这种情况下,“aarea.ary\[50\]”和“aarea.num\[25\]”使用相同的存储器定位。也就是“aarea”联合或者用“char”型数组,或者用“int”型数组访问。 “str1”结构的22字节大小是联合的最大成员,就是“tableu”类型的大小。在这个例子里,说明了联合数组“datatable\[20\]”。“datatable”联合数组的大小是“str1”结构的大小乘以20。在这种情况下,记录的大小是“str1”结构的22字节。20个记录可以用“str1”或“str2”访问。 下面解释联合是怎样用来保留变量存储空间的。 源程序: #define OK 1 #define ERR -1 union ldata{ char array\[100\]; int num\[25\]; }local; extern int getval(void); extern int getarray(char); extern int getnum(int); int list() { char key; int total; int i; total=getval(); if(getarray(local.array)){ key=local.array\[10\]; if(getnum(local.num)){ for(i=0;i!=25;i++) { total+=1; if(total>key) return(OK); else return(total); } } } return(ERR); } 映像文件: D:000BH〖〗PUBLIC〖〗local〖〗D:0008H〖〗SYMBOL〖〗key〖〗D:0009H〖〗SYMBOL〖〗total〖〗D:0004H〖〗SYMBOL〖〗i列表文件: MODULE INFORMATION:STATIC OVERLAYABLE CODE SIZE=99\ \ \ \ CONSTANT SIZE=\ \ \ \ \ \ \ \ XDATA SIZE=\ \ \ \ \ \ \ \ PDATA SIZE=\ \ \ \ \ \ \ \ DATA SIZE=1003 IDATA SIZE=\ \ \ \ \ \ \ \ BIT SIZE=\ \ \ \ \ \ \ \ END OF MODULE INFORMATION. 正如上面所述,作为联合的存储区域可以以不同的数据类型访问,这个特点可以用来减少变量占用的存储空间。编写程序时,定义成外部变量的任何变量都要在RAM中占用存储空间。“并不是所有外部变量需要同时存在”,一旦函数完成,大多数工作变量的值就不需要了。 这个例子中,“getarray()”函数完成的进程是以字符值处理数组,“getnum()”函数是以整数值处理数组。因为2个函数处理的是同样的数据,因而不能定义成局部变量。占用的RAM区域只有从0BH开始的100字节。 在以文本(字符类型数据)读取时,没有整数类型的数值读取,而在数值读取时,没有文本读取。在这种情况下,没有必要同时存在“char”类型和“int”类型数组,如下所示: (1) 定义成常规数组 char array\[100\];/* 100 bytes */ int num\[25\];/* 50 bytes */ (2) 定义成联合 union ldata{ char array\[100\];/* 100 bytes */ int num\[25\];/* 50 bytes */ }local; 数组分别定义成字符和整数数组作为外部变量,在RAM中,要有不同的存储空间存在,总共占用150字节。通过定义成联合,只要占用100字节,就可以保留RAM空间。(待续)
来源:单片机与嵌入式系统应用 作者:北京理工大学 马忠梅 2006/2/12 0:00:00