单片机应用技术 第6章 C语言程序设计及仿真调试

上传人:M****1 文档编号:568746378 上传时间:2024-07-26 格式:PPT 页数:81 大小:161KB
返回 下载 相关 举报
单片机应用技术 第6章 C语言程序设计及仿真调试_第1页
第1页 / 共81页
单片机应用技术 第6章 C语言程序设计及仿真调试_第2页
第2页 / 共81页
单片机应用技术 第6章 C语言程序设计及仿真调试_第3页
第3页 / 共81页
单片机应用技术 第6章 C语言程序设计及仿真调试_第4页
第4页 / 共81页
单片机应用技术 第6章 C语言程序设计及仿真调试_第5页
第5页 / 共81页
点击查看更多>>
资源描述

《单片机应用技术 第6章 C语言程序设计及仿真调试》由会员分享,可在线阅读,更多相关《单片机应用技术 第6章 C语言程序设计及仿真调试(81页珍藏版)》请在金锄头文库上搜索。

1、单片机应用技术1/81第第6 6章章 C C语言程序设计及仿真调试语言程序设计及仿真调试 6.1 6.1 C C语言程序设计基础语言程序设计基础 6.2 6.2 Keil C Keil C 对对 ANSI CANSI C的扩展的扩展 单片机应用技术2/816.1 6.1 C C语言程序设计基础语言程序设计基础 6.1.1 6.1.1 数据类型数据类型 1 1基本数据类型基本数据类型 基本数据类型不可以再分解为其他类型。基本数据类型不可以再分解为其他类型。 2 2构造数据类型构造数据类型 数组类型数组类型 结构类型结构类型 联合类型联合类型 3 3指针类型指针类型 用来表示某个量在内存中的地址。

2、用来表示某个量在内存中的地址。 4 4空类型空类型 调用后不需要向调用者返回值的函数为调用后不需要向调用者返回值的函数为“空类型空类型”。其类。其类型说明符为型说明符为voidvoid。 单片机应用技术3/816.1.2 6.1.2 基本运算符和表达式基本运算符和表达式 1 1运算符的种类运算符的种类 丰富的运算符和表达式使丰富的运算符和表达式使C C语言功能十分完善。这也是其主要特点语言功能十分完善。这也是其主要特点之一。之一。 C C语言的运算符不仅具有不同的优先级,而且还具有结合性。在表语言的运算符不仅具有不同的优先级,而且还具有结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运

3、算符优先级别的达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还是自右向左进行运算。是自右向左进行运算。C C语言的运算符可分为以下几类:语言的运算符可分为以下几类: 算术运算符:算术运算符:用于各类数值运算。包括加(用于各类数值运算。包括加(+ +)、减()、减(- -)、乘)、乘()、除()、除(/ /)、求余(或称模运算,)、求余(或称模运算,% %)、自增()、自增(+)、自减()、自减(-)共七种。)共七种。 关系运算符:关系运算符:用于比较运算。包括大于

4、(用于比较运算。包括大于( )、小于()、小于( =)、小于等于()、小于等于(=)和不等于()和不等于(!=!=)六种。)六种。 逻辑运算符:逻辑运算符:用于逻辑运算。包括与(用于逻辑运算。包括与(&)、或()、或(|)、非()、非(! !)三种。三种。 位操作运算符:位操作运算符:参与运算的量,按二进制位进行运算。包括位与参与运算的量,按二进制位进行运算。包括位与(& &)、位或()、位或(| |)、位非()、位非( )、位异或()、位异或( )、左移()、左移()六种。)六种。单片机应用技术4/81 赋值运算符:赋值运算符:用于赋值运算,分为简单赋值(用于赋值运算,分为简单赋值(= =)

5、、复合算术赋值)、复合算术赋值(+=+=,-=-=,*=*=,/=/=,%=%=)和复合位运算赋值()和复合位运算赋值(&=&=,|=|=,=,=,=)三类共十一种。)三类共十一种。 条件运算符:条件运算符:这是一个三目运算符,用于条件求值(这是一个三目运算符,用于条件求值(?:?:)。)。 逗号运算符:逗号运算符:用于把若干表达式组合成一个表达式(,)。用于把若干表达式组合成一个表达式(,)。 指针运算符:指针运算符:用于取内容(用于取内容(* *)和取地址()和取地址(& &)二种运算。)二种运算。 求字节数运算符:求字节数运算符:用于计算数据类型所占的字节数(用于计算数据类型所占的字节数

6、(sizeofsizeof)。)。 特殊运算符:特殊运算符:有括号有括号( )( ),下标,下标 ,成员,成员(,.).)等几种。等几种。单片机应用技术5/812. 2. 优先级和结合性优先级和结合性 C C语言中,运算符的运算优先级共分为语言中,运算符的运算优先级共分为1515级级。1 1级最高,级最高,1515级最低。在表达式中,优先级较高的先于优先级较低的级最低。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,进行运算。而在一个运算量两侧的运算符优先级相同时, 则按运算符的结合性所规定的结合方向处理。则按运算符的结合性所规定的结合方向处理。 C

7、C语言中各语言中各运算符的结合性分为两种,即左结合性(自左至右)和右运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如算术运算符的结合性是自左至结合性(自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如表达式右,即先左后右。如表达式x-y+zx-y+z,则则y y应先与应先与“-”“-”号结合,号结合, 执行执行x-yx-y运算,然后再执行运算,然后再执行+ +z z的运算。这种自左至右的结合的运算。这种自左至右的结合方向就称为方向就称为“左结合性左结合性”。而自右至左的结合方向称为。而自右至左的结合方向称为“右结合性右结合性”。 最典型的右结合性运算符是赋值

8、运算符。如最典型的右结合性运算符是赋值运算符。如x=y=z,x=y=z,由于由于“=”“=”的右结合性,应先执行的右结合性,应先执行y=zy=z再执行再执行x=(y=z)x=(y=z)运算。运算。 单片机应用技术6/813. 3. 算术运算符和算术表达式中基本的算术运算符算术运算符和算术表达式中基本的算术运算符 加法运算符加法运算符“+”“+”:加法运算符为双目运算符,即应有加法运算符为双目运算符,即应有两个量参与加法运算。如两个量参与加法运算。如a+b,4+8a+b,4+8等。具有右结合性。等。具有右结合性。 减法运算符减法运算符“-”“-”:减法运算符为双目运算符。但减法运算符为双目运算符

9、。但“-”“-”也可作负值运算符,此时为单目运算,如也可作负值运算符,此时为单目运算,如- -x x,-5-5等具有左结等具有左结合性。合性。 乘法运算符乘法运算符“*”“*”:双目运算,具有左结合性。双目运算,具有左结合性。 除法运算符除法运算符“/”“/”:双目运算具有左结合性。参与运算双目运算具有左结合性。参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。一个是实型,则结果为双精度实型。 求余运算符(模运算符)求余运算符(模运算符)“%”“%”:双目运算,具有左结双目运算,具有左结合性。要求参与

10、运算的量均为整型。合性。要求参与运算的量均为整型。 求余运算的结果等于求余运算的结果等于两数相除后的余数。两数相除后的余数。 单片机应用技术7/814. 4. 自增,自减运算符自增,自减运算符 自增自增1 1运算符记为运算符记为“+”“+”,其功能是使变量的值自增,其功能是使变量的值自增1 1。自减自减1 1运算符记为运算符记为“-”“-”,其功能是使变量值自减,其功能是使变量值自减1 1。自增。自增1 1,自减,自减1 1运算符均为单目运算,都具有右结合性。可有以运算符均为单目运算,都具有右结合性。可有以下几种形式:下几种形式: + +i i:i i自增自增1 1后再参与其他运算。后再参与其

11、他运算。 - -i i:i i自减自减1 1后再参与其他运算。后再参与其他运算。 i+ i+:i i参与运算后,参与运算后,i i的值再自增的值再自增1 1。 i- i-:i i参与运算后,参与运算后,i i的值再自减的值再自减1 1。单片机应用技术8/815. 5. 算术表达式算术表达式 算术表达式是由算术运算符和括号连接起来的算术表达式是由算术运算符和括号连接起来的式子,以下是算术表达式的例子:式子,以下是算术表达式的例子: a+b a+b,(a*2)(a*2)c c,(x+r)*8-(a+b)(x+r)*8-(a+b)7 7,+i+i,sin(x)+sin(y)sin(x)+sin(y)

12、,(+i)-(j+)+(k-)(+i)-(j+)+(k-)。 单片机应用技术9/816. 6. 赋值运算符和赋值表达式赋值运算符和赋值表达式 简单赋值运算符和表达式,简单赋值运算符记为简单赋值运算符和表达式,简单赋值运算符记为“=”“=”。由。由“= “= ”连接的式子称为赋值表达式。其一般形式为:变量连接的式子称为赋值表达式。其一般形式为:变量= =表达式。例表达式。例如:如: x=a+b x=a+b w=sin(a)+sin(b) w=sin(a)+sin(b) 如果赋值运算符两边的数据类型不相同,系统将自动进行类型如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边

13、的类型换成左边的类型。具体规定如下:转换,即把赋值号右边的类型换成左边的类型。具体规定如下: 实型赋予整型,舍去小数部分。整型赋予实型,数值不变,但实型赋予整型,舍去小数部分。整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分将以浮点形式存放,即增加小数部分( (小数部分的值为小数部分的值为0)0)。 字符型赋予整型,由于字符型为一个字节,而整型为二个字节,字符型赋予整型,由于字符型为一个字节,而整型为二个字节,故将字符的故将字符的ASCIIASCII码值放到整型量的低八位中,高八位为码值放到整型量的低八位中,高八位为0 0。 整型赋予字符型,仅把低八位赋予字符量。整型赋予字符型,仅把

14、低八位赋予字符量。单片机应用技术10/817. 7. 复合赋值符及表达式复合赋值符及表达式 在赋值符在赋值符“=”“=”之前加上其他双目运算符可构成复合之前加上其他双目运算符可构成复合赋值符。如:赋值符。如: += +=,-=-=,*=*=,= =,%=%=,=,&=&=,=,|=|= 构成复合赋值表达式的一般形式为:构成复合赋值表达式的一般形式为: 变量变量 双目运算符双目运算符= =表达式表达式 它等效于:它等效于: 变量变量= =变量变量 运算符运算符 表达式表达式 例如:例如:a+=5a+=5等价于等价于a=a+5a=a+5,x*=y+7x*=y+7等价于等价于x=x*(y+7)x=x

15、*(y+7),r%=p r%=p 等价于等价于r=r%pr=r%p等。复合赋值符这种写法,对初学等。复合赋值符这种写法,对初学者可能不习惯,者可能不习惯, 但十分有利于编译处理,能提高编译效但十分有利于编译处理,能提高编译效率并产生质量较高的目标代码。率并产生质量较高的目标代码。单片机应用技术11/818. 8. 逗号运算符逗号运算符 C C语言中逗号语言中逗号“,”也是一种运算符,称为逗号运也是一种运算符,称为逗号运算符。其功能是把两个表达式连接起来组成一个表达式,算符。其功能是把两个表达式连接起来组成一个表达式,称为逗号表达式。其一般形式为:称为逗号表达式。其一般形式为: 表达式表达式1

16、1,表达式,表达式2 2; 其求值过程是分别求两个表达式的值,并以表达式其求值过程是分别求两个表达式的值,并以表达式2 2的值作为整个逗号表达式的值。的值作为整个逗号表达式的值。 单片机应用技术12/816.1.3 6.1.3 C C语言程序设计语言程序设计 从程序流程的角度来看,程序可以分为三种基本从程序流程的角度来看,程序可以分为三种基本结构,即:结构,即: 顺序结构顺序结构 分支结构分支结构 循环结构循环结构 这三种基本结构可以组成所有的各种复杂程序。这三种基本结构可以组成所有的各种复杂程序。语言提供了多种语句来实现这些结构。语言提供了多种语句来实现这些结构。单片机应用技术13/811.

17、 1. 分支结构程序分支结构程序 (1 1)ifif语句语句 用用ifif语句可以构成分支结构。它根据给定的条件进行判断,语句可以构成分支结构。它根据给定的条件进行判断,以决定执行某个分支程序段。以决定执行某个分支程序段。C C语言的语言的ifif语句有三种基本形式。语句有三种基本形式。 第一种形式为基本形式:第一种形式为基本形式: if ( if (表达式表达式) ) 语句语句 其语义是:如果表达式的值为真,则执行其后的语句,否则其语义是:如果表达式的值为真,则执行其后的语句,否则不执行该语句。不执行该语句。 第二种形式为:第二种形式为:if-elseif-else if( if(表达式表达

18、式) ) 语句语句1 1; else else 语句语句2 2; 其语义是:如果表达式的值为真,则执行语句其语义是:如果表达式的值为真,则执行语句1 1,否则执行语,否则执行语句句2 2 。单片机应用技术14/81 第三种形式为:第三种形式为:if-else-ifif-else-if 前二种形式的前二种形式的ifif语句一般都用于两个分支的情况。当有多个分支选择语句一般都用于两个分支的情况。当有多个分支选择时,可采用时,可采用if-else-ifif-else-if语句,其一般形式为:语句,其一般形式为: if( if(表达式表达式) ) 语句语句1 1; else if( else if(表

19、达式表达式2)2) 语句语句2 2; else if( else if(表达式表达式3)3) 语句语句3 3; else if( else if(表达式表达式m)m) 语句语句m m; else else 语句语句n n; 其语义是:依次判断表达式的值,当出现某个值为真时,则执行其对其语义是:依次判断表达式的值,当出现某个值为真时,则执行其对应的语句。然后跳到整个应的语句。然后跳到整个ifif语句之外继续执行程序。如果所有的表达式均为语句之外继续执行程序。如果所有的表达式均为假,则执行语句假,则执行语句n n。然后继续执行后续程序。然后继续执行后续程序。单片机应用技术15/81(2 2)swi

20、tchswitch语句语句 C C语言还提供了另一种用于多分支选择的语言还提供了另一种用于多分支选择的switchswitch语句,其一般语句,其一般形式为:形式为: switch( switch(表达式表达式) ) case case常量表达式常量表达式1: 1: 语句体语句体1;1; case case常量表达式常量表达式2: 2: 语句体语句体2;2; case case常量表达式常量表达式n: n: 语句体语句体n;n; default: default: 语句体语句体n+1;n+1; 其语义是:计算表达式的值,并逐个与其后的常量表达式值相其语义是:计算表达式的值,并逐个与其后的常量表

21、达式值相比较,当表达式的值与某个常量表达式的值相等时,即执行其后的比较,当表达式的值与某个常量表达式的值相等时,即执行其后的语句体。一般语句体中包含语句体。一般语句体中包含breakbreak语句,所以执行完语句体后,不语句,所以执行完语句体后,不再进行判断其他的再进行判断其他的casecase语句,直接执行语句,直接执行switchswitch语句后面的程序。如语句后面的程序。如表达式的值与所有表达式的值与所有casecase后的常量表达式均不相同时,则执行后的常量表达式均不相同时,则执行defaultdefault后的语句体。后的语句体。 单片机应用技术16/812. 2. 循环结构程序循

22、环结构程序 循环结构是程序中一种很重要的结构。其特点是,循环结构是程序中一种很重要的结构。其特点是,在给定条件成立时,反复执行某程序段,直到条件不成在给定条件成立时,反复执行某程序段,直到条件不成立为止。给定的条件称为循环条件,反复执行的程序段立为止。给定的条件称为循环条件,反复执行的程序段称为循环体。语言提供了多种循环语句,可以组成各称为循环体。语言提供了多种循环语句,可以组成各种不同形式的循环结构。种不同形式的循环结构。 单片机应用技术17/81(1 1)whilewhile语句语句 whilewhile语句的一般形式为:语句的一般形式为: while( while(表达式表达式) ) 语

23、句;语句; 其中表达式是循环条件,语句为循环体。其中表达式是循环条件,语句为循环体。 while while语句的语义是:计算表达式的值,当值为真语句的语义是:计算表达式的值,当值为真(非(非0 0)时,)时, 执行循环体语句。执行循环体语句。 单片机应用技术18/81(2)2)dodowhilewhile语句语句 do-whiledo-while语句的一般形式为:语句的一般形式为: do do 语句;语句; while( while(表达式表达式) ); 其中语句是循环体,表达式是循环条件。其中语句是循环体,表达式是循环条件。 do-while do-while语句的语义是:先执行循环体语句

24、一次,再判别表达语句的语义是:先执行循环体语句一次,再判别表达式的值,若为真(非式的值,若为真(非0 0)则继续循环,否则终止循环。)则继续循环,否则终止循环。 do-while do-while语句和语句和whilewhile语句的区别在于语句的区别在于do-whiledo-while是先执行后判是先执行后判断,因此断,因此do-whiledo-while至少要执行一次循环体。而至少要执行一次循环体。而whilewhile是先判断后执是先判断后执行,如果条件不满足,则一次循环体语句也不执行。行,如果条件不满足,则一次循环体语句也不执行。whilewhile语句和语句和do-whiledo-w

25、hile语句一般都可以相互改写。语句一般都可以相互改写。单片机应用技术19/81(3) 3) forfor语句语句 forfor语句是语句是C C语言所提供的功能更强,使用更广泛的一种循环语句。其一般语言所提供的功能更强,使用更广泛的一种循环语句。其一般形式为:形式为: for( for(表达式表达式1 1;表达式;表达式2 2;表达;表达3)3) 语句;语句; 其中,表达式其中,表达式1 1通常用来给循环变量赋初值,一般是赋值表达式。也允许在通常用来给循环变量赋初值,一般是赋值表达式。也允许在forfor语句外给循环变量赋初值,此时可以省略该表达式。语句外给循环变量赋初值,此时可以省略该表达

26、式。 表达式表达式2 2通常是循环条件,一般为关系表达式或逻辑表达式。通常是循环条件,一般为关系表达式或逻辑表达式。 表达式表达式3 3通常可用来修改循环变量的值,一般是赋值语句。通常可用来修改循环变量的值,一般是赋值语句。 这三个表达式都可以是逗号表达式,这三个表达式都可以是逗号表达式, 即每个表达式都可由多个表达式组成。即每个表达式都可由多个表达式组成。三个表达式都是任选项,都可以省略。三个表达式都是任选项,都可以省略。 一般形式中的一般形式中的“语句语句”即为循环体语句。即为循环体语句。forfor语句的语义是:语句的语义是: 1. 1.首先计算表达式首先计算表达式1 1的值。的值。 2

27、. 2.再计算表达式再计算表达式2 2的值,若值为真(非的值,若值为真(非0 0)则执行循环体一次,否则跳出循环。)则执行循环体一次,否则跳出循环。 3. 3.然后再计算表达式然后再计算表达式3 3的值,转回第的值,转回第2 2步重复执行。在整个步重复执行。在整个forfor循环过程中,循环过程中,表达式表达式1 1只计算一次,表达式只计算一次,表达式2 2和表达式和表达式3 3则可能计算多次。循环体可能多次执行,则可能计算多次。循环体可能多次执行,也可能一次都不执行。也可能一次都不执行。单片机应用技术20/813. 3. 转移语句转移语句 程序中的语句通常总是按顺序方向,或按语句功能程序中的

28、语句通常总是按顺序方向,或按语句功能所定义的方向执行的。如果需要改变程序的正常流向,所定义的方向执行的。如果需要改变程序的正常流向,可以使用转移语句。在语言中提供了可以使用转移语句。在语言中提供了4 4种转移语句:种转移语句: goto,break,continue goto,break,continue和和returnreturn。 其中的其中的returnreturn语句只能出现在被调函数中,用于返语句只能出现在被调函数中,用于返回主调函数。回主调函数。 单片机应用技术21/81 (1 1)gotogoto语句语句 gotogoto语句也称为无条件转移语句,其一般格式如下:语句也称为无条件

29、转移语句,其一般格式如下: goto goto 语句标号;语句标号; 其中语句标号是按标识符规定书写的符号,放在某一语句行的前面,其中语句标号是按标识符规定书写的符号,放在某一语句行的前面,标号后加冒号标号后加冒号( (:) )。语句标号起标识语句的作用,与。语句标号起标识语句的作用,与gotogoto语句配合使用。语句配合使用。 (2 2)breakbreak语句语句 breakbreak语句只能用在语句只能用在switch switch 语句或循环语句中,其作用是跳出语句或循环语句中,其作用是跳出switchswitch语句或跳出本层循环,转去执行后面的程序。由于语句或跳出本层循环,转去执

30、行后面的程序。由于breakbreak语句的转移方向是语句的转移方向是明确的,所以不需要语句标号与之配合。明确的,所以不需要语句标号与之配合。breakbreak语句的一般形式为:语句的一般形式为: break break; 使用使用breakbreak语句可以使循环语句有多个出口,在一些场合下使编程更加语句可以使循环语句有多个出口,在一些场合下使编程更加灵活、方便。灵活、方便。 (3 3)continuecontinue语句语句 continuecontinue语句只能用在循环体中,其一般格式是:语句只能用在循环体中,其一般格式是: continue continue; 其语义是:结束本次循

31、环,即不再执行循环体中其语义是:结束本次循环,即不再执行循环体中continue continue 语句之后的语句之后的语句,转入下一次循环条件的判断与执行。应注意的是,本语句只结束本语句,转入下一次循环条件的判断与执行。应注意的是,本语句只结束本层本次的循环,并不跳出循环。层本次的循环,并不跳出循环。单片机应用技术22/816.1.4 6.1.4 函数函数 C C语言程序是由函数组成的。函数是源程序的基本语言程序是由函数组成的。函数是源程序的基本模块,通过对函数模块的调用实现特定的功能。语言中模块,通过对函数模块的调用实现特定的功能。语言中的函数相当于其他高级语言的子程序。语言不仅提供了的函

32、数相当于其他高级语言的子程序。语言不仅提供了极为丰富的库函数,还允许用户建立自己定义的函数。用极为丰富的库函数,还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。可以说程序的全部工作都是用调用的方法来使用函数。可以说程序的全部工作都是由各式各样的函数完成的,所以也把语言称为函数式语由各式各样的函数完成的,所以也把语言称为函数式语言。由于采用了函数模块式的结构,语言易于实现结构言。由于采用了函数模块式的结构,语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、化程序设计。使程序

33、的层次结构清晰,便于程序的编写、阅读、调试。阅读、调试。单片机应用技术23/811 1函数定义的一般形式函数定义的一般形式 (1 1)无参函数的一般形式)无参函数的一般形式 类型说明符类型说明符 函数名函数名()() 类型说明;类型说明; 语句;语句; 其中类型说明符和函数名称为函数头。类型说明符指明了本函其中类型说明符和函数名称为函数头。类型说明符指明了本函数的类型,函数的类型实际上是函数返回值的类型。函数名是由用数的类型,函数的类型实际上是函数返回值的类型。函数名是由用户定义的标识符,函数名后有一个空括号,其中无参数,但括号不户定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。

34、可少。 中的内容称为函数体。在函数体中也有类型说明,这是中的内容称为函数体。在函数体中也有类型说明,这是对函数体内部所用到的变量的类型说明。在很多情况下都不要求无对函数体内部所用到的变量的类型说明。在很多情况下都不要求无参函数有返回值,此时函数类型符可以写为参函数有返回值,此时函数类型符可以写为voidvoid。单片机应用技术24/81(2 2)有参函数的一般形式)有参函数的一般形式 类型说明符类型说明符 函数名函数名( (形式参数类型说明表形式参数类型说明表) ) 类型说明;类型说明; 语句;语句; 有参函数比无参函数多了两个内容,其一是形式参数表,其二有参函数比无参函数多了两个内容,其一是

35、形式参数表,其二是形式参数类型说明。在形参表中给出的参数称为形式参数,它们是形式参数类型说明。在形参表中给出的参数称为形式参数,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值。形参既然是变量,当时,主调函数将赋予这些形式参数实际的值。形参既然是变量,当然必须给以类型说明。然必须给以类型说明。 在程序中是通过对函数的调用来执行函数体的,其过程与其他在程序中是通过对函数的调用来执行函数体的,其过程与其他语言的子程序调用相似。语言中,函数调用的一般形式为:语言的子程序调用相似。语言中,函数

36、调用的一般形式为: 函数名函数名( (实际参数表实际参数表) ); 对无参函数调用时则无实际参数表。实际参数表中的参数可以对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数,变量或其他构造类型数据及表达式。各实参之间用逗号分是常数,变量或其他构造类型数据及表达式。各实参之间用逗号分隔。隔。 单片机应用技术25/812. 2. 函数的参数和函数的值函数的参数和函数的值 (1 1)函数的参数)函数的参数 函数的参数分为形参和实参两种。形参出现在函数函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不定义中,在整个函数体内都可以使用,离开该函数则不能

37、使用。实参出现在主调函数中,进入被调函数后,实能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。的形参从而实现主调函数向被调函数的数据传送。单片机应用技术26/81(2 2)函数的值)函数的值 函数的值是指函数被调用之后,执行函数体中的程序段所取得的并函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。对函数的值(或称函数返回值)有以下一

38、些说明:返回给主调函数的值。对函数的值(或称函数返回值)有以下一些说明: a. a. 函数的值只能通过函数的值只能通过returnreturn语句返回主调函数。语句返回主调函数。 return return 语句的一般形式为:语句的一般形式为: return return 表达式;表达式; 或者为:或者为: return ( return (表达式表达式) ); 该语句的功能是计算表达式的值,并返回给主调函数。在函数中允该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个许有多个returnreturn语句,但每次调用只能有一个语句,但每次调用只能有一个return return

39、 语句被执行,因此语句被执行,因此只能返回一个函数值。只能返回一个函数值。 b. b. 函数值的类型和函数定义中函数的类型应保持一致。如果两者不函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准,自动进行类型转换。一致,则以函数类型为准,自动进行类型转换。 c. c. 如函数值为整型,在函数定义时可以省去类型说明。如函数值为整型,在函数定义时可以省去类型说明。 d. d. 不返回函数值的函数,可以明确定义为不返回函数值的函数,可以明确定义为“空类型空类型”,类型说明符,类型说明符为为“void”void”。 单片机应用技术27/813. 3. 函数的嵌套调用函数的嵌

40、套调用 语言中不允许作嵌套的函数定义。因此各函数之间是平行的,语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是语言允许在一个函数不存在上一级函数和下一级函数的问题。但是语言允许在一个函数的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。即在被调函数中又调用其他函数。即在被调函数中又调用其他函数。 4. 4. 函数的递归调用函数的递归调用 一个函数在它的函数体内调用它自身称为递归调用。这种函数称一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。语言允许函数的递归调

41、用。在递归调用中,主调函数为递归函数。语言允许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身。每调用一次就进入又是被调函数。执行递归函数将反复调用其自身。每调用一次就进入新的一层。新的一层。5. 5. 变量的作用域变量的作用域 在讨论函数的形参变量时曾经提到,形参变量只在被调用期间才在讨论函数的形参变量时曾经提到,形参变量只在被调用期间才分配内存单元,调用结束立即释放。这一点表明形参变量只有在函数分配内存单元,调用结束立即释放。这一点表明形参变量只有在函数内才是有效的,离开该函数就不能再使用了。这种变量有效性的范围内才是有效的,离开该函数就不能再使用了。这种变

42、量有效性的范围称变量的作用域。不仅对于形参变量,语言中所有的量都有自己的称变量的作用域。不仅对于形参变量,语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。语言中的变量,作用域。变量说明的方式不同,其作用域也不同。语言中的变量,按作用域范围可分为两种,按作用域范围可分为两种, 即局部变量和全局变量。即局部变量和全局变量。单片机应用技术28/81 指针是语言中广泛使用的一种数据类型。运用指针编程是语指针是语言中广泛使用的一种数据类型。运用指针编程是语言最主要的风格之一。利用指针变量可以表示各种数据结构;能很方言最主要的风格之一。利用指针变量可以表示各种数据结构;能很方便地使用数

43、组和字符串;并能像汇编语言一样处理内存地址,从而编便地使用数组和字符串;并能像汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了语言的功能。学习指针是出精练而高效的程序。指针极大地丰富了语言的功能。学习指针是学习语言中最重要的一环,能否正确理解和使用指针是我们是否掌学习语言中最重要的一环,能否正确理解和使用指针是我们是否掌握语言的一个标志。握语言的一个标志。 既然指针变量的值是一个地址,那么这个地址不仅可以是变量的既然指针变量的值是一个地址,那么这个地址不仅可以是变量的地址,也可以是其他数据结构的地址。在一个指针变量中存放一个数地址,也可以是其他数据结构的地址。在一个指针变量

44、中存放一个数组或一个函数的首地址有何意义呢?因为数组或函数都是连续存放的。组或一个函数的首地址有何意义呢?因为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址,也就找到了该数组或通过访问指针变量取得了数组或函数的首地址,也就找到了该数组或函数。这样一来,凡是出现数组、函数的地方都可以用一个指针变量函数。这样一来,凡是出现数组、函数的地方都可以用一个指针变量来表示,只要该指针变量中赋予数组或函数的首地址即可。这样做,来表示,只要该指针变量中赋予数组或函数的首地址即可。这样做,将会使程序的概念十分清楚,程序本身也精练、高效。在语言中,将会使程序的概念十分清楚,程序本身也精练、高效

45、。在语言中,一种数据类型或数据结构往往都占有一组连续的内存单元。用一种数据类型或数据结构往往都占有一组连续的内存单元。用“地址地址”这个概念并不能很好地描述一种数据类型或数据结构,而这个概念并不能很好地描述一种数据类型或数据结构,而“指针指针”虽然实际上也是一个地址,但它却是一个数据结构的首地址,它是虽然实际上也是一个地址,但它却是一个数据结构的首地址,它是“指向指向”一个数据结构的,因而概念更为清楚,表示更为明确。这也是一个数据结构的,因而概念更为清楚,表示更为明确。这也是引入引入“指针指针”概念的一个重要原因。概念的一个重要原因。 单片机应用技术29/81 在在C C语言中规定,一个函数总

46、是占用一段连续的内存语言中规定,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针可以找到并调用这个函数。我们把这种指向函数的指针变量称为变量称为“函数指针变量函数指针变量”。 函数指针变量定义的一般形式为:函数指针变量定义的一般形式为: 类型说明符类型说明符 (* (*指针变量名指

47、针变量名)();)(); 其中其中“类型说明符类型说明符”表示被指函数的返回值的类型。表示被指函数的返回值的类型。“(* “(* 指针变量名指针变量名)”)”表示表示“*”“*”后面的变量是定义的指针后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。变量。最后的空括号表示指针变量所指的是一个函数。例如:例如:int (*pf)();int (*pf)();表示表示pfpf是一个指向函数入口的指针是一个指向函数入口的指针变量,该函数的返回值变量,该函数的返回值( (函数值函数值) )是整型。是整型。单片机应用技术30/816.1.6 6.1.6 结构与联合结构与联合 1. 1

48、. 结构类型定义和结构变量说明结构类型定义和结构变量说明 在实际问题中,一组数据往往具有不同的数据类型。例如,在实际问题中,一组数据往往具有不同的数据类型。例如, 在学在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,语言中给出了另一种构造数于编译系统

49、处理。为了解决这个问题,语言中给出了另一种构造数据类型据类型“结构结构”。 它相当于其他高级语言中的记录。它相当于其他高级语言中的记录。 “ “结构结构”是一种构造类型,它是由若干是一种构造类型,它是由若干“成员成员”组成的。每一个组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构既是一种成员可以是一个基本数据类型或者又是一个构造类型。结构既是一种“构造构造”而成的数据类型,那么在说明和使用之前必须先定义它,也而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。就是构造它。如同在说明和调用函数之前要先定义函数一样。 单片机应

50、用技术31/81(1 1)结构的定义)结构的定义 定义一个结构的一般形式为:定义一个结构的一般形式为: struct struct 结构名结构名 成员表列;成员表列; ; ; 成员表由若干个成员组成,每个成员都是该结构成员表由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其的一个组成部分。对每个成员也必须作类型说明,其形式为:形式为: 类型说明符类型说明符 成员名成员名; ; 成员名的命名应符合标识符的书写规定。成员名的命名应符合标识符的书写规定。单片机应用技术32/81例如:例如: struct student struct student int num;

51、int num; char name20; char name20; char sex; char sex; float score; float score; ; ; 在这个结构定义中,结构名为在这个结构定义中,结构名为studentstudent,该结构由该结构由4 4个成员组成。个成员组成。第一个成员为第一个成员为numnum,整型变量;第二个成员为整型变量;第二个成员为namename,字符数组;第三字符数组;第三个成员为个成员为sexsex,字符变量;第四个成员为字符变量;第四个成员为scorescore,实型变量。应注意在实型变量。应注意在括号后的分号是不可少的括号后的分号是不可少

52、的。结构定义之后,即可进行变量说明。凡说。结构定义之后,即可进行变量说明。凡说明为结构明为结构studentstudent的变量都由上述的变量都由上述4 4个成员组成。由此可见,结构是一个成员组成。由此可见,结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。单片机应用技术33/81(2 2)结构类型变量的说明)结构类型变量的说明 说明结构变量有以下三种方法。以上面定义的说明结构变量有以下三种方法。以上面定义的studentstudent为例来加以说明。为例来加以说明。a.a.先定义结构,再说明结构变量。先定义结构,再

53、说明结构变量。 如:如: structstruct student student intint num; num; char name20; char name20; char sex; char sex; float score; float score; ; ;structstruct student boy1,boy2; student boy1,boy2; 说明了两个变量说明了两个变量boy1boy1和和boy2boy2为为studentstudent结构类型。结构类型。单片机应用技术34/81也可以用宏定义使一个符号常量来表示一个结构类型,例如:也可以用宏定义使一个符号常量来表示一

54、个结构类型,例如: # #define STU struct studentdefine STU struct student STU STU int num; int num; char name20; char name20; char sex; char sex; float score; float score; ; ; STU boy1,boy2; STU boy1,boy2;单片机应用技术35/81b. b. 在定义结构类型的同时说明结构变量。在定义结构类型的同时说明结构变量。 例如:例如: struct student struct student int num; int nu

55、m; char name20; char name20; char sex; char sex; float score; float score; boy1,boy2; boy1,boy2;单片机应用技术36/81c. c. 直接说明结构变量。直接说明结构变量。 例如:例如: struct struct int num; int num; char name20; char name20; char sex; char sex; float score; float score; boy1,boy2; boy1,boy2; 表示结构变量成员的一般形式是:表示结构变量成员的一般形式是: 例如:

56、例如:boy1.num boy1.num 即第一个人的学号;即第一个人的学号;boy2.sex boy2.sex 即第二个人的即第二个人的性别。如果成员本身又是一个结构则必须逐级找到最低级的成员才性别。如果成员本身又是一个结构则必须逐级找到最低级的成员才能使用。例如:能使用。例如:boy1.birthday.month boy1.birthday.month 即第一个人出生的月份成员即第一个人出生的月份成员可以在程序中单独使用,与普通变量完全相同。可以在程序中单独使用,与普通变量完全相同。单片机应用技术37/81(3 3)结构变量的赋值)结构变量的赋值 前面已经介绍,结构变量的赋值就是给各成员

57、赋值。前面已经介绍,结构变量的赋值就是给各成员赋值。可用输入语句或赋值语句来完成。可用输入语句或赋值语句来完成。(4 4)结构变量的初始化)结构变量的初始化 如果结构变量是全局变量或为静态变量,则可对它如果结构变量是全局变量或为静态变量,则可对它作初始化赋值。对局部或自动结构变量不能作初始化赋作初始化赋值。对局部或自动结构变量不能作初始化赋值。值。单片机应用技术38/812. 2. 联合联合 “联合联合”与与“结构结构”有一些相似之处。但两者有本质有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构上的不同。在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员

58、长度之和。而在变量的总长度是各成员长度之和。而在“联合联合”中,各成中,各成员共享一段内存空间,一个联合变量的长度等于各成员中员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。应该说明的是,这里所谓的共享不是指把多最长的长度。应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内,而是指该联合变量可被个成员同时装入一个联合变量内,而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。如前面介绍的旧值。如前面介绍的“单位单位”变量,如定义为一个可装入变量,如定义为一个可装入“班级班级”或或“教研室教研

59、室”的联合后,就允许赋予整型值(班的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后,才能把变量说量的说明一个联合类型必须经过定义之后,才能把变量说明为该联合类型。明为该联合类型。单片机应用技术39/81(1 1)联合的定义)联合的定义 定义一个联合类型的一般形式为:定义一个联合类型的一般形式为: union union 联合名联合名 成员表;成员表; ; ; 成员表中含有若干成员

60、,成员的一般形式为:成员表中含有若干成员,成员的一般形式为: 类型说明符类型说明符 成员名。成员名成员名。成员名的命名应符合标识符的规定。例如:的命名应符合标识符的规定。例如: union perdata union perdata int class; int class; char office10; char office10; ; ; 定义了一个名为定义了一个名为perdataperdata的联合类型,它含有两个成员,一个为整型,成员的联合类型,它含有两个成员,一个为整型,成员名为名为classclass;另一个为字符数组,数组名为另一个为字符数组,数组名为officeoffice。联

61、合定义之后,即可进行联联合定义之后,即可进行联合变量说明,被说明为合变量说明,被说明为perdataperdata类型的变量,可以存放整型量类型的变量,可以存放整型量classclass或存放字符数或存放字符数组组officeoffice。单片机应用技术40/81(2 2)联合变量的说明)联合变量的说明 联合变量的说明和结构变量的说明方式相同,也有三种形式。即先定义,联合变量的说明和结构变量的说明方式相同,也有三种形式。即先定义,再说明;定义同时说明和直接说明。以再说明;定义同时说明和直接说明。以perdataperdata类型为例,说明如下:类型为例,说明如下: union perdatau

62、nion perdata int class; int class; char officae10; char officae10; ; ; union perdata a,b; /* union perdata a,b; /*说明说明a,ba,b为为perdataperdata类型类型*/*/ 或者可同时说明为:或者可同时说明为: union perdataunion perdata int class; int class; char office10; char office10; a,b; a,b; 或直接说明为:或直接说明为: unionunion int class; int cl

63、ass; char office10; char office10; a,b a,b; 经说明后的经说明后的a,ba,b变量均为变量均为perdataperdata类型。类型。a a,b b变量的长度应等于变量的长度应等于 perdata perdata 的的成员中最长的长度,即等于成员中最长的长度,即等于officeoffice数组的长度,共数组的长度,共1010个字节。个字节。a a,b b变量如赋予变量如赋予整型值时,只使用了整型值时,只使用了2 2个字节,而赋予字符数组时,可用个字节,而赋予字符数组时,可用1010个字节。个字节。 单片机应用技术41/81(3 3)联合变量的赋值和使用

64、)联合变量的赋值和使用 对联合变量的赋值,使用都只能是对变量的成员进对联合变量的赋值,使用都只能是对变量的成员进行。联合变量的成员表示为:行。联合变量的成员表示为: 例如,例如,a a被说明为被说明为perdataperdata类型的变量之后,可使用类型的变量之后,可使用 和和a.officea.office。不允许只用联合变量名作赋值或其他操作。不允许只用联合变量名作赋值或其他操作。也不允许对联合变量作初始化赋值,赋值只能在程序中也不允许对联合变量作初始化赋值,赋值只能在程序中进行。还要再强调说明的是,一个联合变量,每次只能进行。还要再强调说明的是,一个联合变量,每次只能赋予一个成员值。换句

65、话说,一个联合变量的值就是联赋予一个成员值。换句话说,一个联合变量的值就是联合变量的某一个成员值。合变量的某一个成员值。单片机应用技术42/816.1.7 6.1.7 枚举和位运算枚举和位运算 1. 1. 枚举枚举 在实际问题中,有些变量的取值被限定在一个有限的在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月,范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。如果把这些量说明为整型,一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其他类型显然是不妥当的。为此,语言提供了字符型或其他类型显然是不妥当的。为此,语

66、言提供了一种称为一种称为“枚举枚举”的类型。在的类型。在“枚举枚举”类型的定义中列举类型的定义中列举出所有可能的取值,被说明为该出所有可能的取值,被说明为该“枚举枚举”类型的变量取值类型的变量取值不能超过定义的范围。应该说明的是,枚举类型是一种基不能超过定义的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。任何基本类型。单片机应用技术43/81(1 1)枚举类型的定义和枚举变量的说明)枚举类型的定义和枚举变量的说明 a. a. 枚举的定义枚举的定义 枚举类型定义的一般形式为:枚举类型定义的一般

67、形式为: enum enum 枚举名枚举名 枚举值表枚举值表; 在枚举值表中应罗列出所有可用值。这些值也称为在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。枚举元素。例如:例如: enum weekday enum weekday sun,mou,tue,wed,thu,fri,sat; sun,mou,tue,wed,thu,fri,sat; 该枚举名为该枚举名为weekdayweekday,枚举值共有枚举值共有7 7个,即一周中的个,即一周中的七天。凡被说明为七天。凡被说明为weekdayweekday类型变量的取值只能是七天类型变量的取值只能是七天中的某一天。中的某一天。单片机应用

68、技术44/81b. b. 枚举变量的说明枚举变量的说明 如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。设有变量说明或直接说明。设有变量a,b,ca,b,c被说明为上述的被说明为上述的weekdayweekday,可采用下述任一种方式:可采用下述任一种方式: enum weekday enum weekday . . ; ; enum weekday a,b,c; enum weekday a,b,c;或者为:或者为: enum weekday enum weekday . .

69、a,b,c; a,b,c;或者为:或者为: enum enum . . a,b,c; a,b,c;单片机应用技术45/81(2 2)枚举类型变量的赋值和使用)枚举类型变量的赋值和使用 枚举类型在使用中有以下规定:枚举类型在使用中有以下规定: a. a. 枚举值是常量,不是变量。枚举值是常量,不是变量。不能在程序中用赋值语句不能在程序中用赋值语句再对它赋值。例如对枚举再对它赋值。例如对枚举weekdayweekday的元素再作以下赋值:的元素再作以下赋值:sun=5;mon=2;sun=mon;sun=5;mon=2;sun=mon;都是错误的。都是错误的。 b. b. 枚举元素本身由系统定义了

70、一个表示序号的数值,从枚举元素本身由系统定义了一个表示序号的数值,从0 0 开始顺序定义为开始顺序定义为0 0,1 1,2 2,。如在如在weekdayweekday中,中,sunsun值为值为0 0,monmon值为值为1 1,satsat值为值为6 6。 c. c. 只能把枚举值赋予枚举变量,不能把元素的数值直接只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。赋予枚举变量。如:如:a=sum;b=mon; a=sum;b=mon; 是正确的。而:是正确的。而:a=0;b=1; a=0;b=1; 是错误的。如一定要把数值赋予枚举变量,则必须用强制类是错误的。如一定要把数值赋予枚举

71、变量,则必须用强制类型转换,如:型转换,如: a=(enum weekday)2;a=(enum weekday)2;其意义是将顺序号为其意义是将顺序号为2 2的的枚举元素赋予枚举变量枚举元素赋予枚举变量a a,相当于:相当于:a=tue; a=tue; 还应该说明的是还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。双引号。 单片机应用技术46/812. 2. 位运算位运算 前面介绍的各种运算都是以字节作为最基本位进行的。前面介绍的各种运算都是以字节作为最基本位进行的。但在很多系统程序中常要求在位(但在很多系统程

72、序中常要求在位(bitbit)一级进行运算或一级进行运算或处理。语言提供了位运算的功能,这使得语言也能像处理。语言提供了位运算的功能,这使得语言也能像汇编语言一样用来编写系统程序。位运算符语言提供了汇编语言一样用来编写系统程序。位运算符语言提供了六种位运算符:六种位运算符:& & 按位与按位与| | 按位或按位或 按位异或按位异或 取反取反 右移右移单片机应用技术47/81(1 1)按位与运算)按位与运算 按位与运算符按位与运算符“&”“&”是双目运算符。其功能是参与运算是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为的两数各对应的二进位相与。只有对应的两个二进

73、位均为1 1时,结果位才为时,结果位才为1 1,否则为,否则为0 0。参与运算的数以补码方式出现。参与运算的数以补码方式出现。 例如,例如,9&59&5可写算式如下:可写算式如下: 00001001 (9 00001001 (9的二进制补码的二进制补码)&00000101 (5)&00000101 (5的二进制补码的二进制补码) )00000001 (100000001 (1的二进制补码的二进制补码) ) 可见可见9&5=19&5=1。 按位与运算通常用来对某些位清零或保留某些位。例如按位与运算通常用来对某些位清零或保留某些位。例如把把a a 的高八位清零,保留低八位,的高八位清零,保留低八位

74、, 可作可作a&255a&255运算运算(255(255的二的二进制数为进制数为0000000011111111)0000000011111111)。 单片机应用技术48/81(2 2)按位或运算)按位或运算 按位或运算符按位或运算符“|”“|”是双目运算符。其功能是参与运是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有算的两数各对应的二进位相或。只要对应的二个二进位有一个为一个为1 1时,结果位就为时,结果位就为1 1。参与运算的两个数均以补码出。参与运算的两个数均以补码出现。现。 例如,例如,9|59|5可写算式如下:可写算式如下: 00001001|0000

75、0101=00001101 ( 00001001|00000101=00001101 (十进制为十进制为13)13) 可见可见9|5=139|5=13单片机应用技术49/81(3 3)按位异或运算)按位异或运算 按位异或运算符按位异或运算符“ ”是双目运算符。其功能是参是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为位相异时,结果为1 1。参与运算数仍以补码出现,例如,。参与运算数仍以补码出现,例如,9595可写成算式如下:可写成算式如下: 0000100100000101 0000100100000101

76、 00001100 ( 00001100 (十进制为十进制为12)12)(4 4)求反运算)求反运算 求反运算符为单目运算符,具有右结合性。其功求反运算符为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位求反。例如,能是对参与运算的数的各二进位按位求反。例如,9 9的运算为:的运算为:单片机应用技术50/81(5 5)左移运算)左移运算 左移运算符左移运算符“”“”是双目运算符。其功能把是双目运算符。其功能把“ ”“ ”左边左边的运算数的各二进位全部左移若干位,由的运算数的各二进位全部左移若干位,由“”“”右边的数指右边的数指定移动的位数,高位丢弃,低位补定移动的位数,高位丢弃,

77、低位补0 0。 例如:例如:a4 a”“”是双目运算符。其功能是把是双目运算符。其功能是把“ ”“ ”左左边的运算数的各二进位全部右移若干位,边的运算数的各二进位全部右移若干位,“”“”右边的数指右边的数指定移动的位数。定移动的位数。 例如:设例如:设 a=15a=15,a2a2表示把表示把000001111000001111右移为右移为0000001100000011( (十进制十进制3)3)。 应该说明的是,对于有符号数,在右移时,符号位将随应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补同移动。当为正数时,最高位补0 0,而为负数时,符号位为,而为负数时,符号

78、位为1 1,最高位是补,最高位是补0 0或是补或是补1 1 取决于编译系统的规定。取决于编译系统的规定。单片机应用技术51/813. 3. 位域位域 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如,在存放一个开关量时,只有或一个二进制位。例如,在存放一个开关量时,只有0 0和和1 1 两种状态,两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,语言又提供用一位二进位即可。为了节省存储空间,并使处理简便,语言又提供了一种数据结构,称为了一种数据结构,称为“位域位域”或或“位段位段”。所谓。所谓“位

79、域位域”是把一个字是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。位域的定义和位域变量的说明位对象用一个字节的二进制位域来表示。位域的定义和位域变量的说明位域定义与结构定义类似,其形式为:域定义与结构定义类似,其形式为: struct struct 位域结构名位域结构名 位域列表位域列表 ; ; 其中位域列表的形式为:其中位域列表的形式为: 类型说明符类

80、型说明符 位域名:位域长度。例如:位域名:位域长度。例如: struct bs struct bs int a:8; int a:8; int b:2; int b:2; int c:6; int c:6; ; ;单片机应用技术52/81 位域变量的说明与结构变量说明的方式相同。可采用先定义后说位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:明,同时定义说明或者直接说明这三种方式。例如: struct bs struct bs int a:8; int a:8; int b:2; int b:2; int c:6; int c:6; dat

81、a; data; 说明说明datadata为为bsbs变量,共占两个字节。其中,位域变量,共占两个字节。其中,位域a a占占8 8位,位域位,位域b b占占2 2位,位域位,位域c c占占6 6位。位。单片机应用技术53/81 对于位域的定义尚有以下几点说明:对于位域的定义尚有以下几点说明: (1 1)一个位域必须存储在同一个字节中,不能跨两个字节。)一个位域必须存储在同一个字节中,不能跨两个字节。如如一个字节所剩空间不够存放另一位域时,从下一单元起存放该位域。一个字节所剩空间不够存放另一位域时,从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:也可以有意使某位域从下一单元开始。

82、例如: struct bs struct bs unsigned a:4 unsigned a:4 unsigned :0 /* unsigned :0 /*空域空域*/*/ unsigned b:4 /* unsigned b:4 /*从下一单元开始存放从下一单元开始存放*/*/ unsigned c:4 unsigned c:4 在这个位域定义中,在这个位域定义中,a a占第一字节的占第一字节的4 4位,后位,后4 4位填位填0 0表示不使用,表示不使用,b b从第二字节开始,占用从第二字节开始,占用4 4位,位,c c占用占用4 4位。位。 (2 2)由于位域不允许跨两个字节,因此位域的

83、长度不能大于一个)由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过字节的长度,也就是说不能超过8 8位二进位。位二进位。单片机应用技术54/81(3 3)位域可以无位域名,这时它只用来作填充或调整)位域可以无位域名,这时它只用来作填充或调整位置。位置。 无名的位域是不能使用的。例如:无名的位域是不能使用的。例如: struct k struct k int a:1 int a:1 int :2 /* int :2 /*该该2 2位不能使用位不能使用*/*/ int b:3 int b:3 int c:2 int c:2 ; ; 从以上分析可以看出,位域在本质上就

84、是一种结构类型,不过从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。其成员是按二进位分配的。单片机应用技术55/81 以以“#”“#”号开头的命令是预处理命令。如包含命令号开头的命令是预处理命令。如包含命令# #includeinclude,宏定义命令宏定义命令# #definedefine等。在源程序中这些命令等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分。为预处理部分。 所谓预处理是指在进行编译的第一遍扫描(词法扫描所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)

85、之前所作的工作。预处理是语言的一个重和语法分析)之前所作的工作。预处理是语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。部分作处理,处理完毕自动进入对源程序的编译。 语言提供了多种预处理功能,如宏定义、文件包含、语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。合理地使用预处理功能编写的程序便于阅读、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块

86、化程序设计。下面介绍修改、移植和调试,也有利于模块化程序设计。下面介绍常用的几种预处理功能。常用的几种预处理功能。单片机应用技术56/811 1宏定义宏定义 在语言源程序中允许用一个标识符来表示一个字符串,称为在语言源程序中允许用一个标识符来表示一个字符串,称为“宏宏”。被定义为。被定义为“宏宏”的标识符称为的标识符称为“宏名宏名”。在编译预处理时,对。在编译预处理时,对程序中所有出现的程序中所有出现的“宏名宏名”,都用宏定义中的字符串去代换,这称为,都用宏定义中的字符串去代换,这称为“宏代换宏代换”或或“宏展开宏展开”。 宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程宏定义是由源程

87、序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在语言中,序自动完成的。在语言中,“宏宏”分为有参数和无参数两种。下面分为有参数和无参数两种。下面分别讨论这两种分别讨论这两种“宏宏”的定义和调用。的定义和调用。 无参宏定义:无参宏定义: 无参宏的宏名后不带参数。其定义的一般形式为:无参宏的宏名后不带参数。其定义的一般形式为:# #define define 标识符标识符 字符串字符串 其中的其中的“#”“#”表示这是一条预处理命令。表示这是一条预处理命令。凡是以凡是以“#”“#”开头的均为开头的均为预处理命令。预处理命令。“define”define”为宏定义命令。为宏定义命令。“标识

88、符标识符”为所定义的宏名。为所定义的宏名。“字符串字符串”可以是常数、表达式、格式串等。符号常量的定义就是一可以是常数、表达式、格式串等。符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。例种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。例如:如:# #define M (y*y+3*y) define M (y*y+3*y) 定义定义M M 表达式表达式( (y*y+3*y)y*y+3*y)。在编写源程序在编写源程序时,所有的时,所有的( (y*y+3*y)y*y+3*y)都可由都可由M M代替,而对源程序作编译时,将先由预代替,而对源程序作编译时,将先

89、由预处理程序进行宏代换,即用处理程序进行宏代换,即用( (y*y+3*y)y*y+3*y)表达式去置换所有的宏名表达式去置换所有的宏名M M,然然后再进行编译。后再进行编译。 单片机应用技术57/81对于宏定义还要说明以下几点:对于宏定义还要说明以下几点: 1 1)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开是表达式

90、,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。后的源程序时发现。 2 2)宏定义不是说明或语句,在行末不加分号。宏定义必须写在函数之外,)宏定义不是说明或语句,在行末不加分号。宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用# # undefundef命命令,例如:令,例如:# #define PI 3.14159define PI 3.14159。 3 3)宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。宏)宏名在源程序中若用引号括起来,则预处理程序不对其作宏

91、代换。宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。预处理程序层层代换。 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。 可用宏定义表示数据类型,使书写方便。例如:可用宏定义表示数据类型,使书写方便。例如: # #define STU struct studentdefine STU struct student 在程序中可用在程序中可用STUSTU作变量说明:作变量说明:STU body5,*p;ST

92、U body5,*p; #define INTEGER int #define INTEGER int 在程序中即可用在程序中即可用INTEGERINTEGER作整型变量说明:作整型变量说明:INTEGER a,b;INTEGER a,b;单片机应用技术58/81 4 4)应注意用宏定义表示数据类型和用)应注意用宏定义表示数据类型和用typedeftypedef定义数据说明符的区定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而别。宏定义只是简单的字符串代换,是在预处理完成的,而typedeftypedef是是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。在

93、编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。请看下面的例子:被命名的标识符具有类型定义说明的功能。请看下面的例子:# #define PIN1 int*define PIN1 int*typedef (int*) PIN2;typedef (int*) PIN2; 从形式上看这两者相似,但在实际使用中却不相同。下面用从形式上看这两者相似,但在实际使用中却不相同。下面用PIN1PIN1,PIN2PIN2说明变量时就可以看出它们的区别:说明变量时就可以看出它们的区别:PIN1 a,b;PIN1 a,b; 在宏代换后变成在宏代换后变成int *a

94、,b;int *a,b;表示表示a a是指向整型的指针变量,而是指向整型的指针变量,而b b是整是整型变量。然而:型变量。然而: PIN2 a,b; PIN2 a,b;表示表示a,ba,b都是指向整型的指针变量。因为都是指向整型的指针变量。因为PIN2PIN2是一个类型是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型,但毕竟是作说明符。由这个例子可见,宏定义虽然也可表示数据类型,但毕竟是作字符代换,使用时要分外小心,以免出错。字符代换,使用时要分外小心,以免出错。 单片机应用技术59/81带参宏定义:带参宏定义: 语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称

95、语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。 带参宏定义的一般形式为:带参宏定义的一般形式为:# #define define 宏名宏名( (形参表形参表) ) 字符串字符串 在字符串中含有各个形参。带参宏调用的一般形式为:宏名在字符串中含有各个形参。带参宏调用的一般形式为:宏名( (实参表实参表) ); 参宏定义中,宏名和形参表之间不能有空格出现。例如把:参宏定义中,宏名和形参表之间不能有空格出现。例如把:# #defin

96、e MAX(a,b) (ab)?a:bdefine MAX(a,b) (ab)?a:b写为:写为:# #define MAX (a,b) (ab)?a:bdefine MAX (a,b) (ab)?a:b 将被认为是无参宏定义,宏名将被认为是无参宏定义,宏名MAXMAX代表字符串代表字符串 ( (a,b) (ab)?a:ba,b) (ab)?a:b。宏展开时,宏宏展开时,宏调用语句:调用语句: max=MAX(x,y); max=MAX(x,y); 将变为:将变为:max=(a,b) (ab)?a:b(x,y);max=(a,b) (ab)?a:b(x,y);这显然是错误的。这显然是错误的。

97、在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行实参值赋予形参,进行“值传递值传递”。而在带参宏中,只是符号代换,不存在值传递的。而在带参宏中,只是符号代换,不存在值传递的问题。在宏定义中的形

98、参是标识符,而宏调用中的实参可以是表达式。在宏定义中,问题。在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。在宏定义中,字符串内的形参通常要用括号括起来以避免出错。带参的宏和带参函数很相似,但有字符串内的形参通常要用括号括起来以避免出错。带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的结果有可能是不同的。宏定义也可用来定义多个语句,在宏调用时,把这些语句又代结果有可能是不同的。宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。换到源程序内。

99、单片机应用技术60/812 2文件包含文件包含 文件包含是文件包含是C C预处理程序的另一个重要功能。文件包含命令行的预处理程序的另一个重要功能。文件包含命令行的一般形式为:一般形式为:# #include include 文件名文件名 例如:例如:# #include include #include math.h #include math.h 文件包含命令的功能是把指定的文件插入该命令行位置取代该命文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,令行, 从而把指定的文件和当前的源程序文件连成一个源文件。在从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中,文件包

100、含是很有用的。一个大的程序可以分为多个模块,程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。这样,一个文件,在其他文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。出错。 单片机应用技术61/81对文件包含命令还要说明以下几点:对文件包含命令还要说明以下几点: (1 1)包含命令中的文件

101、名可以用双引号括起来,也可以用尖括号)包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。例如以下写法都是允许的:括起来。例如以下写法都是允许的:# #include include #include #include 但是这两种形式是有区别的:使用尖括号表示在包含文件目录中但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去

102、查找。用户编程时可根据自己文件所在的目录找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。来选择某一种命令形式。 (2 2)一个)一个includeinclude命令只能指定一个被包含文件,若有多个文件命令只能指定一个被包含文件,若有多个文件要包含,则需用多个要包含,则需用多个includeinclude命令。命令。 (3 3)文件包含允许嵌套,即在一个被包含的文件中又可以包含另)文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。一个文件。单片机应用技术62/813 3条件编译条件编译 预处理程序提供了条件编译的功能。可以按不同的条件去编译预处理程序

103、提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。条件编译有三种形式,下面分别介绍:植和调试是很有用的。条件编译有三种形式,下面分别介绍:(1 1)第一种形式:)第一种形式: # #ifdef ifdef 标识符标识符 程序段程序段1 1 # #else else 程序段程序段2 2 # #endifendif 它的功能是,如果标识符已被它的功能是,如果标识符已被# #definedefine命令定义过,则对程序段命令定义过,则对程序段1 1进行编译;否则对程序段进

104、行编译;否则对程序段2 2进行编译。如果没有程序段进行编译。如果没有程序段2 2(它为空),(它为空),本格式中的本格式中的# #elseelse可以没有。可以没有。 单片机应用技术63/81(2 2)第二种形式:)第二种形式: # #ifndef ifndef 标识符标识符 程序段程序段1 1 # #elseelse 程序段程序段2 2 # #endifendif 与第一种形式的区别是将与第一种形式的区别是将“ifdef”ifdef”改为改为“ifndef”ifndef”。它的功能是,如果标识符它的功能是,如果标识符未被未被# #definedefine命令定义过则对程序段命令定义过则对程序

105、段1 1进行编译,否则对程序段进行编译,否则对程序段2 2进行编译。这与第一进行编译。这与第一种形式的功能正相反。种形式的功能正相反。(3 3)第三种形式:)第三种形式: # #if if 常量表达式常量表达式 程序段程序段1 1 # #elseelse 程序段程序段2 2 # #endifendif 它的功能是,如常量表达式的值为真(非它的功能是,如常量表达式的值为真(非0 0),则对程序段),则对程序段1 1 进行编译,否则对进行编译,否则对程序段程序段2 2进行编译。因此可以使程序在不同条件下,完成不同的功能。上面介绍的条进行编译。因此可以使程序在不同条件下,完成不同的功能。上面介绍的条

106、件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1 1或程或程序段序段2 2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。十分必要的。 单片机应用技术64/816.2 6.2 Keil C Keil C 对对 ANSI CANSI C 的扩展的扩展 当设计一个小

107、的嵌入式系统时一般使用汇编语言。在很多工程中这是一当设计一个小的嵌入式系统时一般使用汇编语言。在很多工程中这是一个很好的方法,因为代码一般都不长,而且都比较简单。使用汇编的麻烦在个很好的方法,因为代码一般都不长,而且都比较简单。使用汇编的麻烦在于它的可读性和可维护性,代码的可重用性也比较低。但是如果使用于它的可读性和可维护性,代码的可重用性也比较低。但是如果使用C C语言语言的话可以很好地解决这些问题。因为的话可以很好地解决这些问题。因为C C 语言有很好的结构性和模块化,更容语言有很好的结构性和模块化,更容易阅读和维护,用易阅读和维护,用C C 语言编写的程序有很好的可移植性。功能化的代码能

108、够语言编写的程序有很好的可移植性。功能化的代码能够很方便地从一个工程移植到另一个工程,从而减少了开发时间。用很方便地从一个工程移植到另一个工程,从而减少了开发时间。用C C 编写程编写程序比汇编更符合人们的思考习惯,开发者可以更专心地考虑算法而不是考虑序比汇编更符合人们的思考习惯,开发者可以更专心地考虑算法而不是考虑一些细节问题,这样就减少了开发和调试的时间。使用像一些细节问题,这样就减少了开发和调试的时间。使用像C C这样的语言的程这样的语言的程序员不必十分熟悉处理器的构造。这意味着对新的处理器也能很快上手。不序员不必十分熟悉处理器的构造。这意味着对新的处理器也能很快上手。不必知道处理器的具

109、体内部结构,使得用必知道处理器的具体内部结构,使得用C C 编写的程序比汇编程序有更好的可编写的程序比汇编程序有更好的可移植性。很多处理器支持移植性。很多处理器支持C C 编译器,所有这些并不说明汇编语言就没了立足编译器,所有这些并不说明汇编语言就没了立足之地,很多系统特别是实时性较高的系统都是用之地,很多系统特别是实时性较高的系统都是用C C 和汇编语言联合编程。并和汇编语言联合编程。并且,当对时钟要求很严格时,使用汇编语言成了唯一的方法。除此之外,包且,当对时钟要求很严格时,使用汇编语言成了唯一的方法。除此之外,包括硬件接口的操作都应该用括硬件接口的操作都应该用C C来编程。来编程。C C

110、的特点就是可以使你尽量少地对硬件的特点就是可以使你尽量少地对硬件进行操作,是一种功能性和结构性很强的语言。进行操作,是一种功能性和结构性很强的语言。 深入理解并应用深入理解并应用C51C51对标准对标准ANSI CANSI C的扩展是学习的扩展是学习C51C51的关键之一。因为大的关键之一。因为大多数扩展功能都是直接针对多数扩展功能都是直接针对80518051系列系列CPUCPU硬件的。硬件的。单片机应用技术65/816.2.6.2.1 Keil C511 Keil C51扩展关键字扩展关键字 C51C51版本有以下扩展关键字版本有以下扩展关键字( (共共1919个个) ):_ _at_ sb

111、it sfr bit sfr16 idata bdata at_ sbit sfr bit sfr16 idata bdata xdata pdata data code alien small compact xdata pdata data code alien small compact large using reentrant interrupt large using reentrant interrupt _task_task_单片机应用技术66/811. 1. 内存区域内存区域( (Memory Areas)Memory Areas): (1) Pragram Area(1)

112、Pragram Area: 由由CodeCode说明可有多达说明可有多达6464kBkB的程序存储器的程序存储器 (2) Internal Data Memory:(2) Internal Data Memory: 内部数据存储器可用以下关键字说明:内部数据存储器可用以下关键字说明: data data:直接寻址区,为内部直接寻址区,为内部RAMRAM的低的低128128字节字节 00 00H H7FH7FH idata idata:间接寻址区,包括整个内部间接寻址区,包括整个内部RAMRAM区区 00 00H H0FFH0FFH bdata bdata:可位寻址区,可位寻址区, 20 20H

113、 H2FH2FH (3) External Data Memory (3) External Data Memory 外部外部RAMRAM视使用情况可由以下关键字标识:视使用情况可由以下关键字标识: xdata xdata:可指定多达可指定多达6464KBKB的外部直接寻址区,地址范围的外部直接寻址区,地址范围00000000H H0FFFFH0FFFFH pdata pdata:能访问能访问1 1页页(256(256Bytes)Bytes)的外部的外部RAMRAM,主要用于紧凑模式主要用于紧凑模式( (Compact Model)Compact Model)。单片机应用技术67/81 (4)

114、 Speciac Function Register Memory(4) Speciac Function Register Memory STC12C5410AD STC12C5410AD单片机提供单片机提供128128B B的的SFRSFR寻址区,可进行位寻址、字节寻址区,可进行位寻址、字节寻址或字寻址,用以控制定时器、计数器、串口、寻址或字寻址,用以控制定时器、计数器、串口、I/OI/O及其他部件,可及其他部件,可由以下几种关键字说明:由以下几种关键字说明: sfr sfr:字节寻址,如字节寻址,如 sfr P0=0x80;sfr P0=0x80;为为POPO口地址为口地址为8080H

115、H,“=”“=”后后0000H H0FFH0FFH之间的常数。之间的常数。 sfr16 sfr16:字寻址,如字寻址,如sfr16 T2=0xcc;sfr16 T2=0xcc;指定指定Timer2Timer2口地址口地址T2L=0xcc T2L=0xcc T2H=0xCDT2H=0xCD。 sbit sbit:位寻址,如位寻址,如sbit EA=0xAF;sbit EA=0xAF;指定第指定第0 0xAFxAF位为位为EAEA,即中断允许。即中断允许。 还可以有如下定义方法:还可以有如下定义方法: sbit 0V=PSW2 sbit 0V=PSW2;( (定义定义0 0V V为为PSWPSW的

116、第的第2 2位位) ) sbit 0V sbit 0V0XDO20XDO2;( (同上同上) ) 或或bit 0V-bit 0V-0xD2(0xD2(同上同上) )。单片机应用技术68/812. 2. _at_关键字关键字 若要实现变量的绝对定位(称为绝对变量),可以直接在数据定义后加若要实现变量的绝对定位(称为绝对变量),可以直接在数据定义后加上上“_at_ 常数地址常数地址”即可,但是注意:即可,但是注意: (1)绝对变量不能被初使化;)绝对变量不能被初使化; (2)bit型函数及变量不能用型函数及变量不能用_at_指定。指定。 例如:例如: unsigned char adcdata i

117、data _at_ 0x40; /指定指定adcdata变量在变量在40H处处 unsigned char buffer20 xdata _at_ 0x0010; /指定指定buffer数组从外部数组从外部RAM的的0010H单元开始单元开始单片机应用技术69/813. 3. 存储模式存储模式 存储模式决定了没有明确指定存储类型的变量,函数参数等的缺省存储区域,共三种:存储模式决定了没有明确指定存储类型的变量,函数参数等的缺省存储区域,共三种: 1. 1. SmallSmall模式模式 所有缺省变量参数均装入内部所有缺省变量参数均装入内部RAMRAM,优点是访问速度快,缺点是空间有限,只适用优

118、点是访问速度快,缺点是空间有限,只适用于小程序。于小程序。2. 2. CompactCompact模式模式 所有缺省变量均位于外部所有缺省变量均位于外部RAMRAM区的一页区的一页(256(256Bytes)Bytes),具体哪一页可由具体哪一页可由P2P2口指定,在口指定,在文件中说明,也可用文件中说明,也可用pdatapdata指定,优点是空间较指定,优点是空间较SmallSmall为宽裕速度较为宽裕速度较SmallSmall慢,较慢,较largelarge要要快,是一种中间状态。快,是一种中间状态。3. 3. largelarge模式模式 所有缺省变量可放在多达所有缺省变量可放在多达64

119、64KBKB的外部的外部RAMRAM区,优点是空间大,可存变量多,缺点是区,优点是空间大,可存变量多,缺点是速度较慢。速度较慢。提示:存储模式在提示:存储模式在C51C51编译器选项中选择。编译器选项中选择。 存储类型声明:存储类型声明: 变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字直接声明指定。变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字直接声明指定。各类型分别用:各类型分别用:code,data,idata,xdata,pdatacode,data,idata,xdata,pdata说明,例:说明,例: data uar1; data uar1; char co

120、de array char code array “hello!”;“hello!”; unsigned char xdata arr1044 unsigned char xdata arr1044;单片机应用技术70/814 4 变量或数据类型变量或数据类型 C51C51提供以下几种扩展数据类型:提供以下几种扩展数据类型: bit bit 位变量值为位变量值为0 0或或1 1 sbit sbit 从字节中定义的位变量从字节中定义的位变量 0 0或或1 1 sfr sfr sfr sfr字节地址字节地址 0 0255255 sfr16 sfr sfr16 sfr字地址字地址 0 0655356

121、5535其余数据类型如:其余数据类型如:char,enum,short,int,long,floatchar,enum,short,int,long,float等与等与ANSI CANSI C相同。相同。单片机应用技术71/81 (1) bit (1) bit型变量型变量 bit bit型变量可用变量类型,函数声明、函数返回值等,存储于内部型变量可用变量类型,函数声明、函数返回值等,存储于内部RAM 20HRAM 20H2FH2FH。 注意:注意: (1) (1) 用用# #pragma disablepragma disable说明函数和用说明函数和用“usigned”usigned”指定的

122、函数,不指定的函数,不能返回能返回bitbit值。值。 (2) (2) 一个一个bitbit变量不能声明为指针,如变量不能声明为指针,如bit *ptrbit *ptr;是错误的是错误的 (3) (3) 不能有不能有bitbit数组如:数组如:bit arr5bit arr5;错误。错误。( (2)2)可位寻址区说明可位寻址区说明 可作如下定义:可作如下定义: int data i int data i; char bdata arr3 char bdata arr3, 然后:然后: sbit bit0 sbit bit0i0i0; sbit bit15=i15 sbit bit15=i15;

123、 sbit arr07=arr07 sbit arr07=arr07; sbit arr15=arr17 sbit arr15=arr17单片机应用技术72/816.2.6.2.2 Keil C512 Keil C51指针指针 C51 C51支持一般指针(支持一般指针(Generic PointerGeneric Pointer)和存储器指针(和存储器指针(Memory_Specific Memory_Specific PointerPointer)。)。一般指针的声明和使用均与标准一般指针的声明和使用均与标准C C相同,不过同时还可以说明指针的相同,不过同时还可以说明指针的存储类型,例如:存

124、储类型,例如: long * state; long * state;为一个指向为一个指向longlong型整数的指针,而型整数的指针,而statestate本身则依存储模式存放。本身则依存储模式存放。 char *data ptr char *data ptr; ptr ptr为一个指向为一个指向charchar数据的指针,而数据的指针,而ptrptr本身放于外部本身放于外部RAMRAM区。以上的区。以上的long,charlong,char等指针指向的数据可存放于任何存储器中。一般指针本身用等指针指向的数据可存放于任何存储器中。一般指针本身用3 3个字节存个字节存放,分别为存储器类型,高位

125、偏移,低位偏移量。基于存储器的指针说明时即指放,分别为存储器类型,高位偏移,低位偏移量。基于存储器的指针说明时即指定了存储类型,例如:定了存储类型,例如: char data * str;str char data * str;str指向指向datadata区中区中charchar型数据型数据 int xdata * pow; pow int xdata * pow; pow指向外部指向外部RAMRAM的的intint型整数型整数 这种指针存放时,只需一个字节或这种指针存放时,只需一个字节或2 2个字节就够了,因为只需存放偏移量。个字节就够了,因为只需存放偏移量。指针转换即指针在上两种类型之间

126、转化:指针转换即指针在上两种类型之间转化: 当基于存储器的指针作为一个实参传递给需要一般指针的函数时,指针自动当基于存储器的指针作为一个实参传递给需要一般指针的函数时,指针自动转化。转化。 如果不说明外部函数原形,基于存储器的指针自动转化为一般指针,将导致如果不说明外部函数原形,基于存储器的指针自动转化为一般指针,将导致错误,因而请用错误,因而请用“#“#include”include”说明所有函数原形。说明所有函数原形。 可以强行改变指针类型。可以强行改变指针类型。单片机应用技术73/816.2.6.2.3 Keil C513 Keil C51函数函数 C51C51函数声明对函数声明对ANS

127、I CANSI C作了扩展,具体包括:作了扩展,具体包括:1. 1. 中断函数声明:中断函数声明: 中断函数通过使用中断函数通过使用 interrupt interrupt 关键字和中断号(关键字和中断号(0 0 到到3131)来声明。中断号告诉编)来声明。中断号告诉编译器中断程序的入口地址,中断号对应着译器中断程序的入口地址,中断号对应着IEIE寄存器中的使能位。寄存器中的使能位。IEIE寄存器中的第寄存器中的第0 0位对应位对应着外部中断着外部中断0 0,相应的外部中断,相应的外部中断0 0的中断号是的中断号是0 0;IEIE寄存器中的第寄存器中的第1 1位对应定时器位对应定时器T0T0,

128、相应,相应的定时器的定时器T0T0的中断号是的中断号是1 1,依此类推。,依此类推。 举例来说,中断声明方法如下:中断声明方法如下: void UART_ISR (void) interrupt 4 using 1 void UART_ISR (void) interrupt 4 using 1 /* ISR */ /* ISR */ 上述代码声明了串行通信中断服务函数。其中上述代码声明了串行通信中断服务函数。其中,interrupt 4 interrupt 4 说明采用了第四个中说明采用了第四个中断向量,断向量,using 1using 1指明采用工作寄存器区指明采用工作寄存器区1 1区。其

129、他中断函数的定义与此类似。区。其他中断函数的定义与此类似。 为提高代码的容错能力,在没用到的中断入口处生成为提高代码的容错能力,在没用到的中断入口处生成retireti语句,定义没用到的中断。语句,定义没用到的中断。 void X0_ISR(void) interrupt 0 / 外部中断外部中断0中断函数中断函数 void T0_ISR (void) interrupt 1 / 定时器定时器T0 中断函数中断函数 void X1_ISR(void) interrupt 2 / 外部中断外部中断1中断函数中断函数 void T1_ISR (void) interrupt 3 / 定时器定时器T

130、1中断函数中断函数 void UART_ISR (void) interrupt 4 / 串行通信中断函数串行通信中断函数 void ADCSPI_ISR (void) interrupt 5 / ADC和和SPI中断函数中断函数 void PCA_ISR (void) interrupt 6 / PCA中断函数中断函数单片机应用技术74/812. 2. 指定工作寄存器区指定工作寄存器区 指定工作寄存器区由指定工作寄存器区由using xusing x声明(声明(x=0x=03 3),),例如:例如: Unsigned char GetKey() using 1 Unsigned char G

131、etKey() using 1 /* /* Your code Your code; */ */ 3.3.指定存储模式指定存储模式 由由smallsmall,compact compact 及及largelarge说明,例如说明,例如: :void fun1(void) small void fun1(void) small 提示:提示:smallsmall说明的函数内部变量全部使用内部说明的函数内部变量全部使用内部RAMRAM。关键的经常性关键的经常性的耗时的地方可以这样声明,以提高运行速度。的耗时的地方可以这样声明,以提高运行速度。单片机应用技术75/81 最多只能有最多只能有3 3个参数

132、通过寄存器传递,规律如表个参数通过寄存器传递,规律如表6-16-1所示。所示。 参数数目参数数目charcharintintlong, long, floatfloat一般指针一般指针1 1R7R7R6 & R7R6 & R7R4R4R7R7R1R1R3R32 2R5R5R4 & R5R4 & R5R4R4R7R7R1R1R3R33 3R3R3R2 & R3R2 & R3R1R1R3R3单片机应用技术76/81 函数返回值一律放于寄存器中。函数返回值一律放于寄存器中。 返回值类型返回值类型寄存器寄存器说明说明bitbitCyCychar/unsigned char char/unsigned

133、char 1_byte 1_byte指针指针R7R7单字节由单字节由R7R7返回返回int/unsigned int int/unsigned int 2_byte 2_byte指针指针R6R6,R7R7R6R6放高位,放高位,R7R7放低位放低位long&unsigned longlong&unsigned longR4R4R7R7R4R4放最高位,放最高位,R7R7放最低位放最低位FloatFloatR4R4R7R7IEEEIEEE标准标准 R7R7放符号位及阶码放符号位及阶码一般指针一般指针R1R1,R2R2,R3R3R3R3放放存存储储空空间间码码,R2R2放放高高位位,R1R1放放低

134、位低位单片机应用技术77/81 可以在函数前声明函数的可重入性,只对一个函数有效。如果声明可以在函数前声明函数的可重入性,只对一个函数有效。如果声明为不可重入的,说明该函数调用过程中将不可被中断。递归或可重入函为不可重入的,说明该函数调用过程中将不可被中断。递归或可重入函数指定在主程序和中断中都可调用的函数,容易产生问题。因为数指定在主程序和中断中都可调用的函数,容易产生问题。因为5151和和PCPC不同,不同,PCPC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而而5151一般使用寄存器传递参数,内部变量一般在一般使用寄存器传递

135、参数,内部变量一般在RAMRAM中,函数重入时会破中,函数重入时会破坏上次调用的数据。可以用以下两种方法解决函数重入坏上次调用的数据。可以用以下两种方法解决函数重入: : 第一种方法:第一种方法: 在相应的函数前使用前述在相应的函数前使用前述“#“#pragma disable”pragma disable”声明,即只允许主程声明,即只允许主程序或中断之一调用该函数。序或中断之一调用该函数。 第二种方法:第二种方法: 将该函数说明为可重入的。如下:将该函数说明为可重入的。如下: void func(param.) reentrant; void func(param.) reentrant;

136、Keil C51 Keil C51编译后将生成一个可重入变量堆栈,然后就可以模拟通过编译后将生成一个可重入变量堆栈,然后就可以模拟通过堆栈传递变量的方法。堆栈传递变量的方法。 由于一般可重入函数由主程序和中断调用,所以通常中断使用与主由于一般可重入函数由主程序和中断调用,所以通常中断使用与主程序不同的工作寄存器组。另外,对可重入函数,在相应的函数前面加程序不同的工作寄存器组。另外,对可重入函数,在相应的函数前面加上开关上开关“#“#pragma noaregs”pragma noaregs”,以禁止编译器使用绝对寄存器寻址,可生以禁止编译器使用绝对寄存器寻址,可生成不依赖于寄存器组的代码。成不

137、依赖于寄存器组的代码。单片机应用技术78/816.2.6.2.4 STC12C5410AD4 STC12C5410AD单片机单片机C51C51程序框架程序框架 #include “stc12.h” 为为STC12C5410AD单片机寄存器定义头文件,其具体内容参见附录单片机寄存器定义头文件,其具体内容参见附录C */void X0_ISR(void) interrupt 0 /外部中断外部中断0服务子程序入口服务子程序入口 /根据需要填入程序代码根据需要填入程序代码void T0_ISR(void) interrupt 1 /定时器定时器0中断服务子程序入口中断服务子程序入口 /根据需要填入程

138、序代码根据需要填入程序代码void X1_ISR(void) interrupt 2 /外部中断外部中断1服务子程序入口服务子程序入口 /根据需要填入程序代码根据需要填入程序代码void T1_ISR(void) interrupt 3 /定时器定时器1中断服务子程序入口中断服务子程序入口 /根据需要填入程序代码根据需要填入程序代码void UART_ISR(void) interrupt 4 /串行通信中断服务子程序入口串行通信中断服务子程序入口 /根据需要填入程序代码根据需要填入程序代码单片机应用技术79/81void ADCSPI_ISR (void) interrupt 5 / ADC

139、和和SPI中断函数中断函数 /根据需要填入程序代码根据需要填入程序代码void PCA_ISR (void) interrupt 6 / PCA和和PWM中断函数中断函数 /根据需要填入程序代码根据需要填入程序代码 void main(void) /此处可存放应用系统的初始化代码此处可存放应用系统的初始化代码 while(1) /主程序循环主程序循环 /根据需要填入适当的内容根据需要填入适当的内容 单片机应用技术80/81【例例6-1】编程实现通过延时函数,由输出方波信号。编程实现通过延时函数,由输出方波信号。解:解:C语言程序如下:语言程序如下:#include “stc12.h” /STC

140、12C5410AD寄存器定义头文件寄存器定义头文件sbit P10=P10;/定义引脚定义引脚void delay(unsigned long cnt) /延时函数延时函数 while(cnt0) cnt-;void main(void) P10=1; while(1) /主程序循环主程序循环 delay(60000); P10=P10; C语言程序的调试方法与第五章中介绍的方法类似,区别仅在于保存程序语言程序的调试方法与第五章中介绍的方法类似,区别仅在于保存程序文件时,后缀名要写为文件时,后缀名要写为“.C”,加入文件时,选择,加入文件时,选择C语言文件。具体的调试过语言文件。具体的调试过程

141、请读者自行进行,在此从略。需要调试与硬件相关的程请读者自行进行,在此从略。需要调试与硬件相关的C语言程序时,需要将语言程序时,需要将程序编译形成程序编译形成.Hex文件,然后下载到单片机中进行运行实验。文件,然后下载到单片机中进行运行实验。 单片机应用技术81/816.3 6.3 思考题思考题1.标准标准C语言中的数据类型有哪几种?语言中的数据类型有哪几种?2.列举并说明列举并说明C语言中的基本运算符。语言中的基本运算符。3.Keil C对对ANSI C进行了哪些扩展?在进行了哪些扩展?在Keil C中如何声明中断函数?中如何声明中断函数?4.用用C语言编写程序:设单片机的系统时钟语言编写程序:设单片机的系统时钟f=12MHz,要求在,要求在脚上输出周期为脚上输出周期为2ms的的方波。方波。5.如何在如何在uVision2集成环境中调试单片机的集成环境中调试单片机的C语言程序?详细叙述调试过程。语言程序?详细叙述调试过程。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 商业/管理/HR > 商业计划书

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号