第4章 51单片机C语言程序设计

51单片机进行软件开发采用的设计语言一般常用汇编语言和C语言,本书主要采用C语言对51单片机进行软件设计,不再介绍汇编语言,同时为了便于初学者能够更好的学习单片机软件设计部分,本章重点对C语言中使用的重点知识进行阐述,读者若想更全面了解C语言知识,可以参考C语言程序设计专门教程。

4.1 C语言简介

C语言是一种结构化语言,能够产生高效率的紧凑代码,具有丰富的运算符和数据类型,可以实现各类复杂的数据结构。C语言不需要了解51单片机的指令系统,初步掌握存储器结构即可,编译器对寄存器分配、存储器寻址以及数据类型等进行管理,不仅用于系统软件的开发,同时也用于应用软件的开发。另外,程序具有规范的结构,提供的库函数包含许多标准子程序,具有较强的数据处理能力,程序易于模块化、便于移植。

编写C语言程序时特点如下:

①一个C语言源程序可以由一个或多个源文件组成。

②C语言程序的扩展名为“.C”。

③每个源文件可由若干函数单元组成,每个函数都是完成某个特殊任务的子程序段。

④一个源程序不论由多少个文件组成,只有唯一一个main函数,即主函数。

⑤程序具有可读性。

⑥编程以及程序调试效率高。

C语言在进行单片机软件设计时使用的词汇有六类:标识符,关键字,运算符,分隔符,常量,注释符等。

1.标识符

C语言规定,标识符只能是字母(A~Z,a~z)、数字(0~9)、下划线()组成的字符串,并且其第一个字符必须是字母或下划线。程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定义外,其余都由用户自定义。在标识符中,大小写是有严格区分的,定义标识符时,取的名字应尽量有直观的意义,便于阅读理解。

2.关键字

关键字是在C语言系统中具有特定意义的字符串,用户定义的标识符名字不能与关键字相同。C语言的关键字分为三类:

(1)类型说明符 用来定义变量、函数或其他数据结构的类型。例如int、float等。

(2)语句定义符 表示一个语句的功能意义。如“switch”“if else”等。

(3)预处理命令字 表示预处理命令的关键字。如例程中用到的“include”。

3.运算符

运算符是编译程序执行特定算术或逻辑操作的符号。C语言有三大运算符:算术、关系与逻辑、位操作。

4.分隔符

C语言中的分隔符有逗号和空格两种。逗号主要用于数据类型说明和函数参数表中分隔变量;空格用于语句各单词之间做间隔符。

5.常量

C语言中使用的常量可分为数字常量、字符常量、字符串常量、符号常量、转义字符常量等几种。

6.注释符

C语言的注释符以“/*”开头到“*/”结尾,之间内容即为注释。编译程序时,注释内容不参与编译,只是起到向用户提示或解释程序意义的作用,同时也为编写、调试、维护程序工作提供了便利。

4.2 数据结构

具有一定格式的数字或者数值为数据,数据的不同格式为数据类型,按照一定的数据类型进行排列、组合、架构称为数据结构。

4.2.1 数据类型

在C语言中,数据类型分为基本类型,构造类型,指针类型,空类型四大类,其中基本类型主要包括整型(int)、字符型(char)、实型也称为浮点型(float)和枚举类型(enum);构造类型主要包括数组类型、结构体类型和共用体类型;指针类型是C语言编写程序的灵魂,也是初学者重点学习C语言的部分;空类型主要针对C语言编写函数时,调用函数后不需要向调用者返回函数值所采用的编程方式。本节对经常用到的数据类型做简单的介绍。

1.整型(int)

int类型长度为两个字节,分为有符号整型(signed int)和无符号整型(unsigned int),默认值为signed int类型。

2.长整型(long)

long类型长度为4个字节,分为有符号长整型(signed long)和无符号长整型(un-signed long),默认值为signed long类型。

3.字符型(char)

char类型长度为1个字节,通常用于定义处理字符数据的变量或常量。分为有符号字符类型(signed char)和无符号字符类型(unsigned char),默认值为signed char类型。

4.浮点型(float)

float类型的长度为32位,一般数学表达式采用浮点数据类型。

5.枚举类型(enum)

enum类型是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。

6.构造数据类型

对一个或多个数据类型用构造的方法定义。一个构造类型的值可以由若干个“成员”或“元素”组成。“成员”是一个基本数据类型或构造类型。在C语言程序设计中,构造类型包括三类:

①数组类型:相同数据类型的元素按一定顺序排列的集合,有限个类型相同的变量用一个名字命名,然后用编号区分他们变量的集合,这个名字成为数组名,编号成为下标。组成数组的各个变量成为数组的分量,也称为数组的元素。

②结构体类型:是一种构造类型的数据,将若干个不同类型的数据变量有序地组合在一起而形成的一种数据集合体。组成该集合体的各个数据变量为结构成员,整个集合体使用一个单独的结构变量名。

③共用体类型:是C语言一种构造类型的数据结构,所占内存空间的长度是其中最长的成员长度,各个成员的数据类型以及长度从同一个地址开始存放,不同的变量分时使用一个内存空间,有效提高内存的利用效率。

7.指针类型

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,也是C语言的精华所在,C语言区别其他程序设计语言的主要特点是处理指针时所表现出的能力和灵活性,正确使用指针类型数据,可以有效的表示复杂的数据结构,直接处理内存地址,合理使用数组。

8.空类型

函数在被调用完成后,通常会返回一个函数值,调用后并不需要向调用者返回函数值,此时,将这种函数定义为“空类型”,其类型说明符为void。

4.2.2 常量与变量

对于基本数据类型量,根据变量值在程序执行过程中是否发生变化,又可分为常量和变量两种。在程序运行过程中值不能改变的量称为常量,而值可以不断变化的量称为变量。

常量——与变量相对应,在程序执行的过程中,其值不能发生改变。常量的数据类型有位型、整型、浮点型、字符型和字符串型。

位型常量为一位二进制值。

整型常量可以为十进制,如1,3,30等;亦可以为十六进制以OX开头,如OX2F、OX5A等。

浮点型常量有十进制和指数两种形式。十进制由数字和小数点组成,如34.44,0.0等,指数形式[±]数字[.数字]e[±]数字,[]中的内容为可选项,其中内容根据具体情况可有可无,但是其余部分必须有,如8e4等。

字符型常量是单引号内的字符,如‘f’,无法显示的控制字符可以在该字符前面加一个反斜杠“\”,组成专用转义字符。转义字符用来表示那些用一般字符不便于表示的语句代码。表4-1为C语言常用的转义字符描述。

表4-1 C语言常用的转义字符描述

978-7-111-47690-0-Part01-134.jpg

(续)

978-7-111-47690-0-Part01-135.jpg

字符串常量由双引号内的字符组成,如“student”等,若双引号内没有字符时为空字符串。在C语言中字符串常量作为字符类型数组来处理,在存储字符串时系统会在字符串尾部加上\0转义字符,作为字符串的结束符。

注意:字符串常量和字符常量区别为“F”:字符串常量;‘F’:字符常量。字符串常量存储时比字符常量多占用一个字节为存储结束符\0。

符号常量在使用之前必须先定义,其一般形式为

#define标识符 常量

其中#define是一条编译预处理命令(预处理命令都以"#"开头),称为宏定义命令,它的功能是把该标识符定义为其后的常量值。习惯上用大写字母来表示符号常量的标识符,用小写字母表示变量标的识符。使用符号常量的优点:如当程序中很多地方都要用到这个变量,而数值又需要经常做改动,这时使用符号常量方便修改。

变量——由两部分构成,一个是变量名,一个是变量值。每个变量都有一个变量名,在内存中占据一定的存储单元,并在该内存单元中存放该变量的值。程序中使用变量须先用标识符作为变量名,并指出所用的数据类型和存储模式。

1.整型变量

变量定义的一般形式为

类型说明符,变量名标识符,变量名标识符,...;

例如:

int a,b,c;(a,b,c为整型变量)

unsigned int a,b;(a,b为无符号整型变量)

定义变量时,允许在一个类型说明符后,同时定义多个相同类型的变量。各变量名之间用逗号间隔,类型说明符与变量名之间至少用一个空格间隔。

2.实型变量

实型变量分为单精度(float型)和双精度(double型)。

其中单精度型占4个字节内存空间,双精度型占8个字节内存空间。

实型变量定义方法与整型变量定义方式相同,

例如:

float a,b;(a,b为单精度实型量)

double a,b;(a,b为双精度实型量)

3.字符变量

字符变量用来存储单个字符,定义处理字符数据的变量。

字符变量定义与整型和实型方式相同,

例如:

char a,b;(a,b为字符型变量)

4.位变量

sbit用于定义可位寻址的对象,如访问特殊功能寄存器中的某位。位变量定义如下:

sbit位变量名=位地址

例如:sbit P1-1=0X91;将位的绝对地址赋值给位变量。

sbit位变量名=特殊功能寄存器名^位位置

例如:sbit P1-1=P1^1;指定位变量名所在位置。

4.3 运算符与表达式

运算符是完成某种特定运算的符号,表达式由运算符以及运算对象所组成的具有特定含义的式子,任意一个表达式后面加一个分号“;”构成表达式语句。按照运算符在表达式中所起的作用分为算术运算符、关系运算符、关系运算符、逻辑运算符和位运算符。

4.3.1 运算符分类

C语言的运算符分7类:

1)算术运算符:用于数值比较运算。

2)关系运算符:用于比较运算。

3)逻辑运算符:用于逻辑运算。

4)位操作运算符:参与运算的量按二进制位进行运算。

5)赋值运算符:用于赋值运算。

6)条件运算符:<表达式1>?<表达式2>:<表达式3>含义为若<表达式1>值为真,则条件表达式取<表达式2>的值;否则取<表达式3>的值。

7)指针运算符:用于取内容“*”和取地址“&”两种运算。

4.3.2 算术运算符与表达式

(1)基本的算术运算符

①加法运算符“+”:如a+b。

②减法运算符/负数值符号“-”:如a-b或负数-8。

③乘法运算符“*”:如a*b。

④除法运算符“/”:如a/b。

⑤求余运算符(模运算符)“%”:如7%4=3,即7除以4后,余数为3。

(2)自增1运算符“++”,其功能使变量的值自增1;自减1运算符为“--”,其功能使变量的值自减1,运算具有左右结合特性有4种形式:

①++i:i自增1后再参与其他运算。

②--i:i自减1后再参与其他运算。

③i++:i参与运算后,i的值再自增1。

④i--:i参与运算后,i的值再自减1。

i++和i--在程序设计时容易出错,尤其程序复杂对于初学者容易搞混淆,注意分辨。

4.3.3 关系运算符与表达式

关系运算符用于比较运算,关系表达式通常是用来判别某个条件是否满足。包括大于(>)、小于(<)、大于等于(≥)、小于等于(≤)、等于(==)和不等于(!=)六种。关系运算符的运算结果只有“0”和“1”两种,当结果为“1”,则表示指定的条件满足,不满足时的结果为“0”。

关系表达式的一般形式为

表达式 关系运算符 表达式

例如:a+b≥c

关系表达式允许出现嵌套表达式。

例如:x!=(a>b)

4.3.4 逻辑运算符和表达式

逻辑运算符用于逻辑运算,C语言提供三种逻辑运算符:

①&&“与”运算

②‖“或”运算

③!“非”运算

其中逻辑运算符优先级从高到低排列如下:!(逻辑非)→&&(逻辑与)→‖(逻辑或),其中,逻辑非的优先级最高,逻辑或的优先级最低。

例如:4>0&&9>2

因为4>0为真,9>2也为真,两边同时满足真,所以它们相“与”的结果也为真。即为“1”。

4.3.5 赋值运算符和表达式

变量赋初值--C语言可以给变量赋初值,称之为变量的初始化操作。

变量初始化赋值的形式为

类型说明符 变量1=值1,变量2=值2,变量3=值3…;

例如:

int a=3;

char channel1='A',channel2='B';

C语言在变量定义中不允许出现多个“=”赋值符号,如a=b=c=5是非法的。

复合赋值运算符—在赋值运算符“=”的前面加上其他运算符构成复合赋值运算符,如下:

+=加法赋值运算符

—=减法赋值运算符

*=乘法赋值运算符

/=除法赋值运算符

%=取模赋值运算符

>>=右移位赋值运算符

<<=左移位赋值运算符

&=逻辑与赋值运算符

︱=逻辑或赋值运算符

^=逻辑异或赋值运算符

978-7-111-47690-0-Part01-136.jpg逻辑非赋值运算符

复合赋值表达式一般形式为

变量 运算符=表达式 等效:变量=变量 运算符 表达式

例如:a+=7等效于a=a+7;a%=b等效于a=a%b

对于初学者复合赋值的写法不太习惯,但是该写法有利于编译程序,提高编译效率。

4.3.6 位运算符与表达式

位运算符是按位对变量进行运算,不改变参与运算的变量的值,注意:位运算符不能用来对浮点型数据进行操作。位运算符的优先级从高到低顺序为

①按位取反978-7-111-47690-0-Part01-137.jpg

②左移(<<)和右移(>>)

③按位与(&)

④按位异或(^)

⑤按位或(︱)

表4-2给出位运算操作真值表。

表4-2 位运算操作逻辑真值表

978-7-111-47690-0-Part01-138.jpg

4.4 函数使用

C语言中的函数就是用来实现模块化程序设计的工具,用户可以把自己的程序写成一个一个相对独立的函数,函数中不能定义其他函数,但是函数可以互相调用,函数可以自己调用自己成为递归调用。函数调用的一般规则是主函数可以调用其他普通函数,普通函数之间可以互相调用,但是普通函数不能调用主函数。

4.4.1 C语言程序的基本结构

C语言程序的一般组成结构如下:

978-7-111-47690-0-Part01-139.jpg

978-7-111-47690-0-Part01-140.jpg

C程序的执行从main()函数开始,不管物理位置上这个main()在什么地方,主函数通过直接书写语句和调用其他功能子函数来实现有关功能,这些功能子函数可以是C语言本身提供的库函数,也可以是用户自己编写的函数。对于初学者函数的学习是C语言程序设计的重点。

4.4.2 函数定义

函数由类型说明符、函数名、参数表和函数体四部分组成。函数名区分大小写;参数表是用括号括起来的若干参数,参数之间用逗号隔开;函数体用大括号括起来的可执行语句,语句与语句之间用分号隔开,最后一条语句为return(主函数中可省略),每个函数都返回数值。

1.无参函数定义

类型标识符 函数名()

978-7-111-47690-0-Part01-141.jpg

其中“类型标识符”指明函数返回值类型。函数名由用户自定义,后面是空括号,代表没有函数参数,即代表无参函数,注意空括号不可以省略。

大括号中的内容被称为函数体。注意:在函数体中有类型说明,函数体中声明的各种对象,只能在函数体内有效。

一般情况下,无参函数没有返回值,因此可以将函数的类型标识符写成“void”。

例如:

978-7-111-47690-0-Part01-142.jpg

978-7-111-47690-0-Part01-143.jpg

上述语句中print为一个无参函数的函数名,并且没有返回值。一旦被调用输出“this is a desk.”字符串。

2.有参函数定义

类型标识符 函数名(形参表)

978-7-111-47690-0-Part01-144.jpg

有参函数比无参函数多了两项,①形参表,②形参类型说明。在该列表中列出的形参称为形式参数,简称为形参,它可以是各种类型的数据,各种参数之间用“,”号分隔。函数被调用时,主调函数将通过实际参数,简称实参,传递实际的值给这些形参。

例如:定义一个有参函数,求两个数中的大数,

978-7-111-47690-0-Part01-145.jpg

程序中第一行定义了一个函数,函数名为big函数,其返回类型定义为整型(int)型,x,y为函数的形参,并且定义int类型。x,y的值由主调函数在调用时传送过来。当big被调用时,主调函数将通过实参将实际的值传给形参x和y。函数体中的if语句用来判断,如果x>y,使用return语句返回x的值,否则就返回y的值。在这里有几点需要注意:

1)有返回值的函数至少应有一个return语句,C语言中,函数可以放在主函数main之前也可以main之后。

2)形参只出现在函数定义中,在函数体中可以被使用,但在函数体外不能使用;实参只出现在主调函数中,在调用函数时,把实参的值传递给被调函数的形参,从而实现主调函数向被调函数的数据传递。

3)函数定义时没有写明类型标识符,则默认为整型。

4)函数返回值return(表达式),其中“表达式”为函数返回主调函数的值,必须和函数的类型标识符一致。

5)函数不需要返回值,函数类型标识符则可以写作“void”,表示该函数没有返回值。

4.4.3 函数调用

所谓的函数调用就是在一个函数体中引用另外一个已经定义的函数,前者为主调函数,后者为被调用函数。C语言中,函数调用一般形式如下:

函数名(实际参数表)

其中“函数名”为被调用的函数

“实际参数表”,即实参表是可以没有的,表示无参函数。实参表可以是任何类型的数据,可以是常量,变量及表达式。各参数之间用“,”分隔。实际参数的作用是将它的值传递给被调用函数中的形式参数。

C语言中采用三种方式完成函数调用分别为①函数语句调用②函数表达式调用③函数参数调用。

1.函数语句调用

主调函数中将函数调用作为一条语句,例如:

max();//无参调用,不要求被调函数返回确定数值。

2.函数表达式调用

主调函数中将函数调用作为一个运算对象直接出现在表达式中,例如:

C=min(a,b);//赋值语句,把min返回值赋给变量C。

这种调用方法要求被调函数返回一个确定值。

3.函数参数调用

函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值。例如:

M=max(a,max(b,c));//max(b,c)是一次函数调用,返回值作为函数max调用的实参。M的值为变量a、b、c中的最大值。该段代码可以看到函数在调用过程中嵌套函数调用,既调用一个函数过程中又调用另一个函数。在函数调用过程中有几点注意事项:

①在函数定义之前,在主函数外部预先说明各个函数的类型,则不需要在后面的主调函数中再对其进行说明。例如:

978-7-111-47690-0-Part01-146.jpg

通过对ann函数和circle函数预先说明。因此在以后各主调函数中不用对ann和circle函数说明,可直接调用。

②对系统库函数的使用或者不在同一文件中自定义函数,必须使用include命令,把函数相应的头文件包含到文本中。

4.4.4 函数的嵌套调用和递归调用

函数嵌套调用—C语言不允许嵌套函数定义,但是允许一个函数的定义中出现对另一个函数的使用,既函数的嵌套调用。通过程序结构了解函数嵌套的编写。如嵌套函数样例程序如下:

978-7-111-47690-0-Part01-147.jpg

程序执行结果为

978-7-111-47690-0-Part01-148.jpg

程序运行过程先执行主函数main,main函数调用max函数,在执行max函数时,max函数调用min函数,min函数执行完毕后输出the min is a,再返回max函数的转出点继续执行剩余代码,max函数执行完毕后输出hello,最后再返回main函数的转出点继续执行剩余代码。

函数递归调用—函数在它的函数体内,调用它自身称为函数的递归调用。C语言是允许递归调用的。在递归调用中,主调函数又是被调函数,执行递归函数将反复调用自身,每调用一次就进入新的一层,一旦条件满足(通过if语句书写条件)终止递归调用,返回结果。

函数递归调用样例程序如下:计算x的n次方(x和n均为正整数)。

978-7-111-47690-0-Part01-149.jpg

978-7-111-47690-0-Part01-150.jpg

在该例中,power函数通过power(x,n-1)直接调用自身,通过变量n控制程序调用的次数,从而实现计算x的n次方。

递归调用的优点是使程序看上去简洁明了,可以使一些原本复杂的程序简化,但是由于函数反复调用占用较大的存储空间,一旦递归调用次数较多,则程序编译效率低,需要优化程序结构。

4.5 数组与指针

数组是一组有序数据的集合,数组中每一个数据都属于同一个数据类型,数组类型的所有元素属于同一种类型,并且按顺序存放在一个连续的存储空间。数组中各个元素用数组名和下标唯一确定。C语言书写程序时需要先定义,然后才能使用。数组可以是一维数组、二维数组或者多维数组。一维数组只有一个下标,多维数组有两个以上下标。

4.5.1 数组

1.一维数组

一维数组定义为:

数据类型 数组名[常量表达式];

其中:

数据类型:数组中各数据元素的类型。

数组名:整个数组的标识,命名方法和变量命名方法是一样的,数组名是所分配空间首地址的标识。

常量表达式:表示数组的长度,既数组中元素个数,必须用“[]”括起,方括号里的数不能含有变量。

例如:

int student[20];//说明整型数组student中有10个学生。

学习数组时有几点需要注意:

①数组的下标是从0开始,如int student[20],下标就是从student[0]到student[19]。

②数组名不能与其他变量名同名。

③C语言允许同一个类型说明中,说明多个数组和多个变量。例如:int a,b,c,s[20]。

④数组定义后,数组中各元素共用一个数组名,通过下标区分各个元素。

2.二维数组

二维数组定义形式:

数据类型 数组名[常量表达式1][常量表达式2]

其中常量表达式1表示第一维大小,常量表达式2表示第二维大小。

定义一个3行3列共3×3=9个元素的整型数组,可以采用二维数组定义 int a[3][3];

其中数组各个元素为

a[0][0],a[0][1],a[0][2]

a[1][0],a[1][1],a[1][2]

a[2][0],a[2][1],a[2][2]

二维数组赋值时,可以分段赋值也可以连续赋值。

例如:对数组a[3][3]赋值:

①按分段进行赋值为int a[3][3]={{8,7,9},{7,6,7},{7,8,7}};

②按连续进行赋值为int a[4][3]={8,7,9,7,6,7,7,8,7};

其结果是一样的。

4.5.2 指针

指针是C语言的知识精华,对于初学C语言读者应当深入学习和掌握指针。更多关于指针知识的学习可以参考C语言相关技术书籍。

1.指针定义

指针用于存放变量的地址,该地址是另一个变量在内存中存储的位置。指针它本身也是一种变量,和其他变量一样,要占有一定数量的存储空间,用来存放指针值(即地址)。

指针定义一般形式为

数据类型*指针变量名;

其中,数据类型:表示该指针变量所指向变量的类型。

指针变量名:定义指针变量的名字。

例如:int*point;指针变量point是指向int类型变量的指针。

注意区分变量的指针和指针变量:变量的指针是变量的地址,而一个指针变量存放的内容是另一个变量在内存中的地址,拥有这个地址的变量为该指针变量所指向的变量。每一个变量都有自己的指针(称之地址),每一个指针变量指向另一个变量。

例如:整型变量x的地址30H存放在指针变量point中,可用*point表示指针变量point指向的变量,即*point表示变量x。

2.指针运算符&和*

指针变量中只能存放地址,基本的运算符是&和*。

(1)&——取地址运算符,返回变量的内存地址。只能用于一个具体的变量或数组元素,不可用于表达式。

例如:

int *p;

int t;

p=&t;

说明将整型变量t的地址赋值给指针变量p。

(2)*——指针运算符,返回地址中的变量值。

例如:

978-7-111-47690-0-Part01-151.jpg

在指针赋值时注意几点:

①指针使用之前,未初始化的指针变量不可以使用。

②赋值语句中,变量的地址只能赋给指针变量本身。

3.指针数组

(1)指针数组的定义 一个数组里面的元素是指针变量称为指针数组。指针数组的一般形式为

类型说明符*指针数组名[元素个数];

例如:

int*p[10];

p[10]是一个指针数组,含有10个指针,并指向int型数据。

(2)指针数组初始化 只有全局或静态的指针数组才可进行初始化,另外,不能用局部变量的地址去初始化静态指针。

例如:指针数组

978-7-111-47690-0-Part01-152.jpg

执行结果:

b[0][0]=1 b[0][1]=2 b[0][2]=3

b[1][0]=4 b[1][1]=5 b[1][2]=6

程序将一个二维数组b[2][3]分解成两个一维数组。它们的首地址,分别为b[0]和b[1],也可以理解为第一行的首地址,和第二行的首地址,并被赋给指针pb[0]和pb[1]。

4.6 程序设计语句

从程序流程的角度看,依据结构化程序设计,结构清晰、层次分明易于修改和阅读。程序结构可以分为顺序结构、分支结构和循环结构三种基本形式。

1.顺序结构

顺序结构是最简单,最基本的编程结构,如图4-1所示。程序由低地址向高地址顺序执行指令代码。程序按照书写顺序依次执行。

2.选择结构

选择结构是对给定的条件进行判断,依据判断结果决定执行哪个分支,如图4-2所示,若条件判断为真,执行语句1,否则执行语句2。

978-7-111-47690-0-Part01-153.jpg

图4-1 顺序结构

978-7-111-47690-0-Part01-154.jpg

图4-2 选择结构

3.循环结构

循环结构是在给定条件成立时反复执行某段程序,有两种形式:①当型循环结构;②直到型循环结构。

当型循环结构如图4-3所示。条件成立时,反复执行语句,直到条件不满足为止。

直到型循环结构如图4-4所示。先执行语句操作,再进行判断,若为假,再执行语句,直到条件为真为止。

978-7-111-47690-0-Part01-155.jpg

图4-3 当型循环结构

978-7-111-47690-0-Part01-156.jpg

图4-4 直到型循环结构

C语言流程控制语句可分成三类:

选择语句:if语句和switch语句。

循环语句:for语句和while语句。

转移语句:break语句和continue语句。

4.6.1 选择语句

1.if语句

条件语句又被称为分支语句,由关键字if构成。根据给定的条件进行判断,决定执行某个分支程序段。C语言提供了3种形式的条件语句:

①基本形式:

if(表达式)

语句;

如果表达式是真就执行后面的语句,否则就不执行。

②if-else形式:

if(表达式)

语句1;

else

语句2;

如果表达式的值为真,则执行语句1,否则执行语句2。

③if-else-if形式:

当有多个分支选择时,采用if-else-if语句,形式为

if(表达式1)

语句1;

else if(表达式2)

语句2;

else if(表达式3)

语句3;

else if(表达式m)

语句m;

else

语句n;

先判断表达式1的值,如果为真,则执行语句1,如果表达式1的值为假,则再判断表达式2的值,如果表达式2的值为真,则执行语句2,否则继续判断表达式3的值,依次判断表达式的值,当出现某个值为真时,则执行后面对应的语句,语句执行完后跳到整个if语句之外继续执行程序代码。如果所有的表达式都为假,那么执行语句n,即最后一个else后面的语句,然后再继续执行后面的程序代码。

if语句若想在满足条件分支时执行多条语句,必须把语句用“{}”括起来,每一条语句末尾需要加上“;”,分号应加在“}”之内,而不能加在“}”外面。

例如:

978-7-111-47690-0-Part01-157.jpg

978-7-111-47690-0-Part01-158.jpg

程序中如果x>0,变量i自加1,输出“x>0”;如果x<0或x=0,变量i自减1,输出“x<=0”。

如果if语句中的执行语句包含if语句,则构成if语句嵌套,采用嵌套结构实质上是为了进行分支选择。在嵌套内的if语句又是if-else型,嵌套内的语句可以是多个if和多个else重叠的情况,对于初学者注意if和else配对问题。

2.switch语句

一般程序有过多的分支时,由于分支太多使程序看起来比较混乱,则使用switch语句,可以使程序结构清晰。switch语句一般形式为

978-7-111-47690-0-Part01-159.jpg

意义是计算switch后面表达式的值,并将其作为条件与case后面的各个常量表达式的值相比,如果相等则执行case后面的语句,再执行break(间断语句)语句,跳出switch语句结构;如果case后面没有和条件相等的值时就执行default后的语句。如果没有符合的条件,不做任何处理,可以不写default语句,default语句只是程序不满足所有case语句条件情况下的一个默认情况执行语句。

使用switch语句时注意以下几点:

①case后的各常量表达式的值是不一样的,否则会出现错误。

②在case后,允许出现多条语句,可以不用{}括起来。

③各case和default语句位置的先后顺序可以改变,而不会影响程序执行结果。

④default子句可以省略不写。

程序举例:用户输入运算数和四则运算符,输出计算结果。

978-7-111-47690-0-Part01-160.jpg

978-7-111-47690-0-Part01-161.jpg

本程序通过输入“+”、“-”、“*”、“/”四个字符,输出对应的运算数据结果为a+b、a-b、a*b和a/b,若输入的字符不是上述四个字符则输出input error,可见多分支程序用switch语句可轻松实现。

4.6.2 循环语句

循环结构在给定条件成立时,反复执行某程序段,直到条件不成立为止。给定的条件为循环条件,反复执行的程序段为循环体。用循环语句书写重复执行语句,不但使程序结构简洁,查看代码显示直观,而且使其编译的效率大大提高。

1.for语句

C语言中的for语句使用非常灵活,不仅可以用于循环次数已定的情况,而且可用于循环次数不确定,只给出循环结束条件的情况。一般形式为

for(初值设定表达式1;循环条件表达式2;条件更新表达式3)

978-7-111-47690-0-Part01-162.jpg

for语句执行过程如下:

①先执行初值设定表达式1的值作为循环控制变量的初值。

②然后求循环条件表达式2的值,当满足循环条件时执行循环体语句并计算更新表达式3。

③然后根据更新表达式3的计算结果判断循环条件2是否满足。

④一直进行到循环条件表达式2的结果为假时,退出循环体。

例如:用for语句计算s=1+2+3+4+……+99+100的结果:

978-7-111-47690-0-Part01-163.jpg

978-7-111-47690-0-Part01-164.jpg

for语句先给变量n赋初值1,判断n是否小于或等于100,如果为真,则执行语句“s=s+n”,然后n自增1。然后再重新判断,直到条件为假,即i>100时,循环结束。

对于使用for语句,要注意以下几点:

①for语句中各表达式可以省略,但是表达式之间的分号间隔符号不能省略。

②省略“循环条件表达式2”,如程序中不做相应的处理,此时应在循环体内结束循环,否则将变成为死循环。

③循环体可以是空语句。

for语句可以构成多重循环,既循环嵌套。所谓循环嵌套,指循环体里面包含了另一个完整的循环。

例如:

978-7-111-47690-0-Part01-165.jpg

程序中使用两层for循环嵌套。其中外层循环变量为i,控制数据的取值范围;内层循环变量为j,内层循环的循环体只有一条语句用于求对应每一个i所有的因子和。

2.while语句

while语句一般形式为

978-7-111-47690-0-Part01-166.jpg

其中表达式为循环条件,语句为循环体。判断表达式是否为真,若为真则执行后面的语句,执行一次完成之后再次回到while后面的表达式,进行判断,如果为真,则重复执行语句,否则跳出循环。当条件一开始就为假时,那么while后面的循环体一次都不会被执行就退出整个循环。

例如:用while语句求s=1+2+3+4+……+99+100。

978-7-111-47690-0-Part01-167.jpg

978-7-111-47690-0-Part01-168.jpg

程序在执行过程中注意几点事项:

①如果第一次进入循环时,while后圆括号内表达式的值为0,循环一次也不执行。在本程序中,如果i的初值大于100,将使表达式i<=100的值为0,循环体也不执行。

②在循环体中一定要有使循环趋向结束的操作,以上循环体内的语句i++使i不断增1,当i>100时,循环结束。如果没有i++;这一语句,则s的值始终不变,进入死循环。

4.6.3 转移语句

程序中的语句通常是按顺序执行,但是需要改变程序的正常流向,可以使用转移语句,例如:如果循环条件需要中途退出循环时,可以考虑采用转移语句退出循环体。

1.break语句

break语句只能用在switch语句或者循环体语句中,其作用在循环体中遇见break语句,立即结束循环,跳到循环体外,执行循环结构后面的语句。break语句的一般形式为

break;

break语句可以使循环体语句有多个出口,使得编程更加灵活和方便。

例如:

978-7-111-47690-0-Part01-169.jpg

程序中当area>50时,使用break跳出循环。

2.continue语句

continue语句是一种中断语句,一般用在循环体中,其功能是结束本次循环,跳过循环体中下面尚未执行的语句,把程序流程转移到当前循环语句的下一个循环周期,并根据循环控制条件决定是否重复执行该循环体。

break是跳出整个循环,continue语句是跳出本次循环,也就是说出现在continue下面语句将不再执行,直接进入下一次循环。

continue的一般格式为:continue;

例如:

978-7-111-47690-0-Part01-170.jpg

978-7-111-47690-0-Part01-171.jpg

程序中,当变量i对5取模不等于0时,则为i不能整除5,此时执行continue语句,程序将跳到for语句继续执行,只有模运算为0时,才能执行后面的printf,输出能被5整除的数据。

4.7 本章小结

本章介绍了C语言的结构与用法,包括数据结构、数组和指针、函数以及程序设计语句的使用方法,其中C语言是51单片机软件编写的重点,通过本章的学习,对于初学单片机的读者掌握C语言流程控制语句设计的方法以及C语言一些基础知识,确保初学者具备基本的C语言软件编写能力。