论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

嵌入式C编程技术

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:37:49

北京理工大学 马忠梅

二、 C语言技巧
这部分内容包括公用表达式处理、指针的使用、循环的使用以及函数返回地址的控制。
1 公用表达式处理
(1) 消除公用表达式
在编写程序完成某些计算的过程中,有时会把同样的运算结果赋值给不止1个变量,
然后在后续的计算中使用。随着程序变大和复杂性的增加,最后可能一次次进行相
同的运算而并不知道。下面是1个把变量i,j,k运算结果赋给变量a,b,c的函数。包
含此函数的程序以列表文件的形式给出,这样可以看到程序的行号、相应行所对应
的汇编程序以及存储器的使用情况。

程序1:
1 void func1(int i,int j)
2 {
3 1 int k=20;
4 1 int a,b,c;
5 1 a=i*j/k;
6 1 b=i*j/k;
7 1 c=i*j/k;
8 1 };

FUNCTIONfunc1 (BEGIN)
0000 8E00RMOV i,R6
0002 8F00RMOV i+01H,R7;
;......|Variable 'j ' assigned to Register 'R2/R3'......
0004 AB05 MOV R3,AR5
0006 AA04 MOV R2,AR4;
SOURCE LINE # 1
; SOURCE LINE # 2
; SOURCE LINE # 3
0008 750000 R MOV k,#00H
000B 750014 R MOV k+01H,#014H;
;SOURCE LINE # 5
000E 120000 E LCALL ? C_IMUL
0011 AC00 R MOV R4,k
0013 AD00 R MOV R5,k+01H
0015 120000 E LCALL ? C_SIDIV
0018 8E00 R MOV a,R6
001A 8F00 R MOV a+01H,R7
;SOURCE LINE # 6
001C 8E00 R MOV b,R6
001E 8F00 R MOV b+01H,R7
;SOURCE LINE # 7
0020 8E00 R MOV c,R6
0022 8F00 R MOV c+01H,R7
;SOURCE LINE # 8
0024 22 RET
; FUNCTION func1 (END)

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 37 ---
CONSTANT SIZE=--- ---
XDATA SIZE=--- ---
PDATA SIZE=--- 10
DATA SIZE=--- ---
IDATA SIZE=--- ---
BIT SIZE=--- ---
END OF MODULE INFORMATION.

程序2:
1 void func2(int i,int j)
2 {
3 int k=20;
4 int a,b,c;
5 a=b=c=i*j/k;
6 }

;FUNCTION func2(BEGIN)
;--- Variable 'i ' assigned to Register 'R6/R7' ---
;--- Variable 'j ' assigned to Register 'R4/R5' ----
;SOURCE LINE # 1
;SOURCE LINE # 2
;SOURCE LINE # 3
0000 750000 R MOV k,#00H
0003 750014 R MOV k+01H,#014H
;SOURCE LINE # 5
0006 120000 E LCALL ? C_IMUL
0009 AC00 R MOV R4,k
000B AD00 RMOV R5,k+01H
000D 120000 E LCALL ?CSIDIV
0010 8E00 R MOV c,R6
0012 8F00 R MOV c+01H,R7
0014 8E00 R MOV b,R6
0016 8F00 R MOV b+01H,R7
0018 8E00 R MOV a,R6
001A 8F00 R MOV a+01H,R7
;SOURCE LINE # 6
001C 22 RET
; FUNCTION_func2 (END)

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 29 ----
CONSTANT SIZE=--- -----
XDATA SIZE=---- ----
PDATA SIZE=--- -----
DATA SIZE=---- 8
IDATA SIZE=---- ----
BIT SIZE=--- -----
END OF MODULE INFORMATION.

程序1的"func1()"有多个赋值表达式,每次(i*j/k)重新计算并把结果赋
给1个变量。在这个函数中,(i*j/k)计算3次,每次产生相同的结果。这样,
非常浪费处理时间。

程序2的"func2()"把多个赋值表达式合并成1个,完成的处理过程如下:首
先,计算(i*j/k);然后,把这个结果赋给变量c,这个结果也赋给变量b和a。
用这种方法,(i*j/k)只计算一次。

值得注重的是:程序1产生的代码是经过优化处理的。好的编译器可以完成公
用表达式的自动消除。这通过使用优化选项(OPTIMIZE)实现。Franklin
C51可支持5级代码优化。OPTIMIZE也可以控制对某一函数的代码优化,如下:

#pragma OPTIMIZE(4)
func( )
{

}
# pragma OPTIMIZE(5)

这样就可以在源程序中控制函数的优化等级。程序1的代码即使是优化生成的,
它也比程序2产生的代码略长。因而,在编写程序过程中要注重消除公用表达式,
以减轻编译程序的负担,提高产生代码的效率。

"消除公用表达式"是减少处理时间的1种方法,它也可以看作是以"减少表达式的
计算次数"来减少处理时间。上面只是1个特例。实际的程序可能是要有相同的计
算,但不是连续出现。这种情况下,应该把相同表达式的处理结果赋给1个变量,
然后,在以后的使用过程中访问这个变量,程序如下:

程序1:
1 void func1(int i,int j)
2 {
3 1 Int k=20;
4 1 int a,b,c;
5 1 a=(i>>4)+j-k;
6 1 b=(i>>4)+j+k;
7 1 c=((i>>4)+j)*k;
8 1}

程序2:
1 void func2(int i,int j)
2 {
3 1 int k=20;
4 1 int a,b,c,d;
5 1 a=(d=(i>>4)+j)-k;
6 1 b=d+k;
7 1 c=d*k;
8 1 }
9 在程序1中,((i>>4)+j)这个表达式被计算多次;而程序2中,((i>>4)+j)的计算
值在第1次出现时就赋给了变量d。以后再碰到相同的计算时,直接使用变量d。

(2) 简化循环
这部分讨论对循环中连续操作的处理以减少循环的运行时间。

在C语言中,"for","while"和"do-while"循环用于重复处理。循环可用于访问1个
变量,完成获得相同结果的计算或进行循环条件的判决。

下面是通过减少不变的处理来减少循环运行时间的方法。

程序1:
1 #define uchar unsigned char
2 #define uint unsigned int
3 #define LEN
4
5 void fun1(uint a,uint b)
6 {
7 1 uchar i;
8 1 uint mem[LEN];
9 1
10 1 for(i=0;i<LEN;i++)
11 1 mem[i]=a/b+10;
12 1 }

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 49 ----
CONSTANT SIZE=---- ----
XDATA SIZE=---- ----
PDATA SIZE=---- ----
DATA SIZE=---- 44
IDATA SIZE=---- ----
BIT SIZE=---- ----
END OF MODULE INFORMATION.

程序2:
1 #define uchar unsigned char
2 #define uint unsigned int
3 #define LEN
4
5 void fun2(uint a,uint b)
6 {
7 1 uchar i;
8 1 uint temp;
9 1 uint mem[LEN] ;
10 1
11 1 temp=a/b+10;
12 1 for(i=0;i<LEN;i++)
13 1 mem[i]=temp;
14 1 }

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 33 ----
CONSTANT SIZE=---- ----
XDATA SIZE=---- ----
PDATA SIZE=---- ----
DATA SIZE=---- 40
IDATA SIZE=---- ----
BIT SIZE=---- ----
END OF MODULE INFORMATION.

程序1的"func1()"在"for"循环中包括1个不变值的赋值表达式"mem[i]=a/b+10"。
由于在"for"循环中变量a和b没有变化,(a/b+10)的计算结果是1个不变值。这个
值没有必要在循环中每次计算1遍。

程序2的"func2()"的不变值表达式已被移到循环的外面。(a/b+10)在循环外计
算后赋给变量"temp"。在"for"循环中使用"temp"变量把值赋给数组元素。这样,
(a/b+10)在func1()中每次循环计算1次,而在func2()中只计算1次。

下面的例子是在"for"循环条件判决时带有1个不变的值。

1 #define uchar unsigned char
2 #define uint unsigned ini
3 #define LEN 20
4
5 void fun1(int num)
6 {
7 1 uchar I;
8 1 uint mem[LEN]
9 1
10 1 for (I=0;I<num*4+2;j++)
11 1 mem[i]=0;
12 1 }

MODULE INFORMATION; STATIC OVERLAYABLE
CODE SIZE= 51 ----
CONSTANT SIZE=--- -----
XDATA SIZE=---- ----
PDATA SIZE=---- ----
DATA SIZE=---- 42
IDATA SIZE=---- ----
BIT SIZE=----- ---
END OF MODULE INFORMATION.


程序2:
1 #define uchar unsigned char
2 #define uint unsigned int
3 #define LEN
4
5 void fun2(int num)
6 {
7 1 Uchar i,a;
8 1 uintmem[LEN];
9 1 for(i=0,a=num*4+2;i<a;i++)
10 1 mem[i]=0;
11 1 }

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 29 ----
CONSTANT SIZE=---- ----
XDATA SIZE=----- ---
PDATA SIZE=---- ----
DATA SIZE=---- 40
IDATA SIZE=----- ---
BIT SIZE=----- ---
END OF MODULE INFORMATION.

程序1中的"func1()",循环条件判决基于(num*4+2)的计算结果。由于在
"for"循环中没有num变量的处理,(num*4+2)是个不变值。对每个循环条件
判决,(num*4+2)的值都被计算1次并和变量i进行比较。(num*4+2)计算
的次数和循环的次数一样多。

程序2中的"func2()",用于循环条件判决的不变表达式从循环中移出来。
"func2()"中,(num*4+2)在初始化处理时把结果赋给了变量a,变量a
再用于循环的判决。"func2()"由于把(num*4+2)的值赋给变量a,节省
了运行时间。

建议大家把循环中的不变值计算移到循环外完成。
2 指针的使用
指针是其值为1个地址的变量。图1是指针的定义和使用。


在这个例子里,定义了"int"类型指针"*intp"和"int"类型变量"idata",然后,
用指针变量改变idata变量的值,过程如下:

· 数值1赋给idata变量,现idata值为1;
· idata变量的地址赋给指针intp;
· 数值2赋给*intp,现idata值为2。

在C语言中,指针可直接用于指定地址。这样就答应存储器中的1个值可被直接访
问或更新。

(1) 指针变量和数组
这一部分解释如何使用指针引用数组元素。数组是同种类型的数据项的收集,数
组保存在留给变量用的连续存储空间中,数组元素0在顶部(最低的)地址。

char Array[10];
char *p;

p=Array;/*指针指向数组Array[0] */
*p=1;/* *p=1使Array[0]为1*/
*(p+1)=1;/**(p+1)=1 使Array[1]为1*/
*(p+2)=1; /**(p+2)=1 使Array[2]为1*/
图2显示如何用1个指针访问定义为"Array[ ]"数组的元素。表明数值1赋给了数组
元素Array[0],Array[1]和Array[2]的过程:

· 定义字符类型数组"Array[ ]"和字符类型指针变量"p";
· 把数组"Array[ ]"的起始地址赋给指针变量"p";
· 赋值1给*p;
· 赋值1给*(p+1);
· 赋值1给*(p+2)。

下面程序比较两个函数:1个使用数组的下标访问数组元素,而另1个使用指针访问
数组元素。

程序1:
1 #define uchar unsigned char
2 #define uint unsigned int
3 #define LEN
4 uchar array1[LEN];
5 uchar array2[LEN];
6
7 void func1()
8
9 1 { uchar I;
10 1 uint sum=0;
11
12 1 for(i=0;i<LEN;i++)
13 1 sum=sum+array1[i]+array2[i];
14 1 }

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 44 ----
CONSTANT SIZE=---- ----
XDATA SIZE=---- ----
PDATA SIZE=---- ----
DATA SIZE=20 2
IDATA SIZE=---- ----
BIT SIZE=---- ----
END OF MODULE INFORMATION.

程序2:
1 #define uchar unsigned char
2 #define uint unsigned int
3 #define LEN 10
4 uchar array1[LEN];
5 uchar array2[LEN];
6
7 void func2()
8 {
9 1 uchar i;
10 uchar *a,*b;
11 1 uint sum=0;
12 1
13 1 for(i=0,a=array1,b=array2;i<LEN;i++){
14 2 sum=sum+(*a)+(*b);
15 2 a++;b++;
16 2 }
17 1 }

MODULE INFORMATION:STATIC OVERLAYABLE
CODE SIZE= 95 ----
CONSTANT SIZE=---- ----
XDATA SIZE=---- ----
PDATA SIZE=---- ----
DATA SIZE=20 9
IDATA SIZE=---- ----
BIT SIZE=----- ---
END OF MODULE INFORMATION.

程序1的func1( )表明完成求和的数组中的数据是用数组的下标访问的。当用数组
的下标访问数组元素时,使用下面的过程计算地址:

· 得到array1[ ]的起始地址;
· 把数组的下标作为偏移量加到起始地址上。

这是计算地址的一般方法。在func1( )中,整个"for"循环当每次执行到这个语句时,
都必须进行地址的计算。而且,array2[ ]的地址也是用相同方法计算出来的。正像
前面所讲,数组元素放在存储器中的连续地址空间。因而,当以数字顺序访问数组
元素时,每次在数组的起始地址上加上偏移量是很花费时间的。

程序2的func2( )是使用指针访问数组。在"for"循环的初始部分把数组array1[ ]和
array2[ ]的起始地址赋给指针变量。在"for"循环中,指针用于计算,然后加1。这
种方法不须要在访问数组元素过程中计算地址。

使用指针和使用数组在功能上是等价的,具体使用哪种方法取决于产生代码的效率。
上一篇:{技巧}一个简易的proxy程序的开发过程(2) 人气:5154
下一篇:{技巧}GTK+ FAQ 人气:6000
视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058