电子工程师自学宝典:嵌入设计篇
上QQ阅读APP看书,第一时间看更新

2.2 C51语言基础

C51语言是指51单片机编程使用的C语言,它与计算机C语言大部分相同,但由于编程对象不同,故两者也略有区别。本节主要介绍C51语言的基础知识,学习时若一时无法理解也没关系,在后续章节有大量的C51编程实例,可在学习这些实例时再到本节查看理解有关内容。

2.2.1 常量

常量是指程序运行时其值不会变化的量。常量类型有整型常量、浮点型常量(也称实型常量)、字符型常量和符号常量。

1.整型常量

1)十进制数:编程时直接写出,如0、18、-6。

2)八进制数:编程时在数值前加“0”表示八进制数,如“012”为八进制数,相当于十进制数的“10”。

3)十六进制数:编程时在数值前加“0x”表示十六进制数,如“0x0b”为十六进制数,相当于十进制数的“11”。

2.浮点型常量

浮点型常量又称实数或浮点数。在C语言中可以用小数形式或指数形式来表示浮点型常量。

1)小数形式表示:由数字和小数点组成的一种实数表示形式,例如0.123、.123、123.、0.0等都是合法的浮点型常量。小数形式表示的浮点型常量必须要有小数点。

2)指数形式表示:这种形式类似数学中的指数形式。在数学中,浮点型常量可以用幂的形式来表示,如2.3026可以表示为0.23026×10^1、2.3026×10^0、23.026×10^-1等形式。在C语言中,则以“e”或“E”后跟一个整数来表示以“10”为底数的幂数。2.3026可以表示为0.23026E1、2.3026e0、23.026e-1。C语言规定,字母e或E之前必须要有数字,且e或E后面的指数必须为整数,如e3、5e3.6、.e、e等都是非法的指数形式。在字母e或E的前后以及数字之间不得插入空格。

3.字符型常量

字符型常量是用单引号括起来的单个普通字符或转义字符。

1)普通字符型常量:用单引号括起来的普通字符,如'b'、'xyz'、'?'等。字符型常量在计算机中是以其代码(一般采用ASCII代码)存储的。

2)转义字符型常量:用单引号括起来的前面带反斜杠的字符,如'\n'、'\xhh'等,其含义是将反斜杠后面的字符转换成另外的含义。表2-2列出一些常用的转义字符及含义。

表2-2 一些常用的转义字符及含义

4.符号常量

在C语言中,可以用一个标识符来表示一个常量,称之为符号常量。符号常量在程序开头定义后,在程序中可以直接调用,在程序中其值不会更改。符号常量在使用之前必须先定义其一般形式为

#define 标识符 常量

例如,在程序开头编写“#define PRICE 25”,就将PRICE被定义为符号常量,在程序中,PRICE就代表25。

2.2.2 变量

变量是指程序运行时其值可以改变的量。每个变量都有一个变量名,变量名必须以字母或下划线“_”开头。在使用变量前需要先声明,以便程序在存储区域为该变量留出一定的空间,比如在程序中编写“unsigned char num=123”,就声明了一个无符号字符型变量num,程序会在存储区域留出一个字节的存储空间,将该空间命名(变量名)为num,在该空间存储的数据(变量值)为123。

变量类型有位变量、字符型变量、整型变量和浮点型变量(也称浮点型变量)。

1)位变量(bit):占用的存储空间为1位,位变量的值为0或1。

2)字符型变量(char):占用的存储空间为1个字节(8位),无符号字符型变量的数值范围为0~255,有符号字符型变量的数值范围为-128~+127。

3)整型变量:可分为短整型变量(int或short)和长整型变量(long),短整型变量的长度(即占用的存储空间)为2个字节,长整型变量的长度为4个字节。

4)浮点型变量:可分为单精度浮点型变量(float)和双精度浮点型变量(double),单精度浮点型变量的长度(即占用的存储空间)为4个字节,双精度浮点型变量的长度为8个字节。由于浮点型变量会占用较多的空间,故单片机编程时尽量少用浮点型变量。

C51变量的类型、长度和取值范围见表2-3。

表2-3 C51变量的类型、长度和取值范围

2.2.3 运算符

C51语言的运算符可分为算术运算符、关系运算符、逻辑运算符、位运算符和复合赋值运算符

1.算术运算符

C51语言的算术运算符见表2-4。在进行算术运算时,按“先乘除模,后加减,括号最优先”的原则进行,即乘、除、模运算优先级相同,加、减优先级相同且最低,括号优先级最高,在优先级相同的运算时按先后顺序进行。

表2-4 C51语言的算术运算符

在C51语言编程时,经常会用到加1符号“++”和减1符号“-”,这两个符号使用比较灵活。常见的用法如下:

y=x++(先将x赋给y,再将x加1)

y=x-(先将x赋给y,再将x减1)

y=++x(先将x加1,再将x赋给y)

y=-x(先将x减1,再将x赋给y)

x=x+1可写成x++或++x

x=x-1可写成x-或-x

%为模运算,即相除取余数运算,例如9%5结果为4。

^为乘幂运算,例如2^3表示2的3次方(23),2^表示2的2次方(22)。

2.关系运算符

C51语言的关系运算符见表2-5。<、>、<=和>=运算优先级高且相同,==、!=运算优先级低且相同,例如“a>b!=c”相当于“(a>b)!=c”。

表2-5 C51语言的关系运算符

用关系运算符将两个表达式(可以是算术表达式、关系表达式、逻辑表达式或字符表达式)连接起来的式子称为关系表达式。关系表达式的运算结果为一个逻辑值,即真(1)或假(0)。

例如:a=4、b=3、c=1,则有

a>b的结果为真,表达式值为1;

b+c<a的结果为假,表达式值为0;

(a>b)==c的结果为真,表达式值为1,因为a>b的值为1,c值也为1;

d=a>b,d的值为1;

f=a>b>c,由于关系运算符的结合性为左结合,a>b的值为1,而1>c的值为0,所以f值为0。

3.逻辑运算符

C51语言的逻辑运算符见表2-6。“&&”“”为双目运算符,要求两个运算对象,“!”为单目运算符,只要有一个运算对象。&&、运算优先级低且相同,!运算优先级高

表2-6 C51语言的逻辑运算符

与关系表达式一样,逻辑表达式的运算结果也为一个逻辑值,即真(1)或假(0)。

例如:a=4、b=5,则有!a的结果为假,因为a=4为真(a值非0即为真),!a即为假(0);ab的结果为真(1);!a&&b的结果为假(0),因为!优先级高于&&,故先运算!a的结果为0,而0&&b的结果也为0。

在进行算术、关系、逻辑和赋值混合运算时,其优先级从高到低依次为:!(非)→算术运算符→关系运算符→&&和→赋值运算符(=)。

4.位运算符

C51语言的位运算符见表2-7。位运算的对象必须是位型、整型或字符型数,不能为浮点型数

表2-7 C51语言的位运算符

位运算举例如下:

5.复合赋值运算符

复合赋值运算符就是在赋值运算符“=”前面加上其他运算符,C51语言常用的复合赋值运算符见表2-8。

表2-8 C51语言常用的复合赋值运算符

复合运算就是变量与表达式先按运算符运算,再将运算结果值赋给参与运算的变量。凡是双目运算(两个对象运算)都可以用复合赋值运算符去简化表达。

复合运算的一般形式为:变量 复合赋值运算符 表达式。例如,a+=28相当于a=a+28。

2.2.4 关键字

在C51语言中,会使用一些具有特定含义的字符串,称为“关键字”,这些关键字已被软件使用,编程时不能将其定义为常量、变量和函数的名称。C51语言关键字分两大类:由ANSI(美国国家标准学会)标准定义的关键字和Keil C51编译器扩充的关键字。

1.由ANSI标准定义的关键字

由ANSI标准定义的关键字有char、double、enum、float、int、long、short、signed、struct、union、unsigned、void、break、case、continue、default、do、else、for、goto、if、return、switch、while、auto、extern、register、static、const、sizeof、typedef、volatile等。这些关键字可分为以下几类:

1)数据类型关键字:用来定义变量、函数或其他数据结构的类型,如unsigned char、int等。

2)控制语句关键字:在程序中起控制作用的语句,如while、for、if、case等。

3)预处理关键字:表示预处理命令的关键字,如define、include等。

4)存储类型关键字:表示存储类型的关键字,如static、auto、extern等。

5)其他关键字:如const、sizeof等。

2. Keil C51编译器扩充的关键字

Keil C51编译器扩充的关键字可分为两类:

1)用于定义51单片机内部寄存器的关键字,如sfr、sbit。

sfr用于定义特殊功能寄存器,如“sfr P1=0x90;”是将地址为0x90的特殊功能寄存器名称定义为P1; sbit用于定义特殊功能寄存器中的某一位,如“sbit LED1=P1^1;”是将特殊功能寄存器P1的第1位名称定义为LED1。

2)用于定义51单片机变量存储类型关键字。这些关键字有6个,见表2-9。

表2-9 用于定义51单片机变量存储类型关键字

2.2.5 数组

数组也常称作表格,是指具有相同数据类型的数据集合。在定义数组时,程序会将一段连续的存储单元分配给数组,存储单元的最低地址存放数组的第一个元素,最高地址存放数组的最后一个元素。

根据维数不同,数组可分为一维数组、二维数组和多维数组;根据数据类型不同,数组可分为字符型数组、整型数组、浮点型数组和指针型数组。在用C51语言编程时,最常用的是字符型一维数组和整型一维数组。

1.一维数组

(1)数组定义

一维数组的一般定义形式为:类型说明符 数组名[下标]。

方括号(又称中括号)中的下标也称为常量表达式,表示数组中的元素个数。

一维数组定义举例如下:

unsigned int a[5];

以上定义了一个无符号整型数组,数组名为a,数组中存放5个元素,元素类型均为整型,由于每个整型数据占2个字节,故该数组占用了10个字节地存储空间,该数组中的第1~5个元素分别用a[0]~a[4]表示。

(2)数组赋值

在定义数组时,也可同时指定数组中的各个元素(即数组赋值),比如:

unsigned int a[5]={2,16,8,0,512};

unsigned int b[8]={2,16,8,0,512};

在数组a中,a[0]=2,a[4]=512;在数组b中,b[0]=2,b[4]=512,b[5]~b[7]均未赋值,全部自动填0。

在定义数组时,要注意以下几点:

1)数组名应与变量名一样,必须遵循标识符命名规则,在同一个程序中,数组名不能与变量名相同。

2)数组中的每个元素的数据类型必须相同,并且与数组类型一致。

3)数组名后面的下标表示数组的元素个数(又称数组长度),必须用方括号括起来,下标是一个整型值,可以是常数或符号常量,不能包含变量。

2.二维数组

(1)数组定义

二维数组的一般定义形式如下:

类型说明符 数组名[下标1][下标2]

其中,下标1表示行数,下标2表示列数。

二维数组定义举例如下:

unsigned int a[2][3];

以上定义了一个无符号整型二维数组,数组名为a,数组为2行3列,共6个元素,这6个元素依次用a[0][0]、a[0][1]、a[0][2]、a[1][0]、a[1][1]、a[1][2]表示。

(2)数组赋值

二维数组赋值有两种方法:

1)按存储顺序赋值。例如:

unsigned int a[2][3]={1,16,3,0,28,255};

2)按行分段赋值。例如:

unsigned int a[2][3]={{1,16,3},{0,28,255}};

3.字符型数组

字符型数组用来存储字符型数据。字符型数组可以在定义时进行初始化赋值。例如:

以上定义了一个字符型数组,数组名为c,数组中存放4个字符型元素(占用了4个字节的存储空间),分别是A、B、C、D(实际上存放的是这4个字母的ASCII码,即0x65、0x66、0x67、0x68)。如果对全体元素赋值时,数组的长度(下标)也可省略,即上述数组定义也可写成:

如果要在字符型数组中存放一个字符串“good”,可采用以下3种方法:

如果要定义二维字符数组存放多个字符串时,二维字符数组的下标1为字符串的个数,下标2为每个字符串的长度,下标1可以不写,下标2则必须要写,并且其值应较最长字符串的字符数(空格也算一个字符)至少多出一个。例如:

上例中的“\n”是一种转义符号,其含义是换行,将当前位置移到下一行开头。

2.2.6 循环语句

在编程时,如果需要某段程序反复执行,可使用循环语句。C51语言的循环语句有3种,即while语句、do while语句和for语句。

1. while语句

while语句的格式为“while表达式){语句组;}”,编程时为了书写阅读方便,一般按以下方式编写:

while语句在执行时,先判断表达式是否为真(非0即为真)或表达式是否成立,若为真或表达式成立则执行大括号(也称花括号)内的语句组(也称循环体),否则不执行大括号内的语句组,直接跳出while语句,执行大括号之后的内容。

在使用while语句时,要注意以下几点:

1)当while语句的大括号内只有一条语句时,可以省略大括号,但使用大括号可使程序更安全可靠。

2)若while语句的大括号内无任何语句(空语句)时,应在大括号内写上分号“;”,即“while表达式){;}”,简写就是“while(表达式);”。

3)如果while语句的表达式是递增或递减表达式,while语句每执行一次,表达式的值就增1或减1。例如“whilei++){语句组;}”。

4)如果希望某语句组无限次循环执行,可使用“while1){语句组;}”。如果希望程序停在某处等待,待条件(即表达式)满足时往下执行,可使用“while表达式);”。如果希望程序始终停在某处不往下执行,可使用“while1);”,即让while语句无限次执行一条空语句。

2. do while语句

do while语句的格式如下:

do while语句在执行时,先执行大括号内的语句组(也称循环体),然后用while判断表达式是否为真(非0即为真)或表达式是否成立,若为真或表达式成立则执行大括号内的语句组,直到while表达式为0或不成立,直接跳出do while语句,执行之后的内容。

do while语句是先执行一次循环体语句组,再判断表达式的真假以确定是否再次执行循环体,而while语句是先判断表达式的真假,以确定是否执行循环体语句组。

3. for语句

for语句的格式如下:

for语句执行过程:先用初始化表达式(如i=0)给变量赋初值,然后判断条件表达式(如i<8)是否成立,不成立则跳出for语句,成立则执行大括号内的语句组,执行完语句组后再执行增量表达式(如i++),接着再次判断条件表达式是否成立,以确定是否再次执行大括号内的语句组,直到条件表达式不成立才跳出for语句。

2.2.7 选择语句

C51语言中常用的选择语句有if语句和switch…case语句。

1. if语句

if语句有三种形式:基本if语句、if…else…语句和if…else if…语句。

(1)基本if语句

基本if语句格式如下:

if语句执行时,首先判断表达式是否为真(非0即为真)或是否成立,若为真或成立则执行大括号(也称花括号)内的语句组(执行完后跳出if语句),否则不执行大括号内的语句组,直接跳出if语句,执行大括号之后的内容。

(2)if…else…语句

if…else…语句格式如下:

if…else…语句执行时,首先判断表达式是否为真(非0即为真)或是否成立,若为真或成立则执行语句组1,否则执行语句组2,执行完语句组1或语句组2后跳出if…else…语句。

(3)if…else if…语句(多条件分支语句)

if…else if…语句格式如下:

if…else if…语句执行时,首先判断表达式1是否为真(非0即为真)或是否成立,为真或成立则执行语句组1,然后判断表达式2是否为真或是否成立,为真或成立则执行语句组2,……最后判断表达式n是否为真或是否成立,为真或成立则执行语句组n,如果所有的表达式都不成立或为假时,跳出if…else if…语句。

2. switch…case语句

switch…case语句格式如下:

switch…case语句执行时,首先计算表达式的值,然后按顺序逐个与各case后面的常量表达式的值进行比较,当与某个常量表达式的值相等时,则执行该常量表达式后面的语句组,再执行break跳出switch…case语句,如果表达式与所有case后面的常量表达式的值都不相等,则执行default后面的语句组,并跳出switch…case语句。