C51语言是指51单片机在编程时使用的C语言,它的语法与C语言大部分相同,但由于编程对象不同,故两者在个别处略有区别。本节主要介绍C51语言的基础知识,若在学习时无法理解某些知识也没关系,在后续章节中还会有大量的C51编程实例,在学习那些实例时再回到本节查看、理解有关内容即可。
常量是指程序运行时其值不会变化的量。常量类型有整型常量、浮点型常量、字符型常量和符号型常量。
1.整型常量
①十进制数:编程时直接写出,如0、18、-6。
②八进制数:编程时在数值前加“0”表示八进制数,如“012”为八进制数,相当于十进制数的“10”。
③十六进制数:编程时在数值前加“0x”表示十六进制数,如“0x0b”为十六进制数,相当于十进制数的“11”。
2.浮点型常量
浮点型常量又称实数或浮点数,可以用小数形式或指数形式来表示。
①小数形式表示:由数字和小数点组成的一种实数表示形式,如0.123、.123、123.、0.0等都是合法的浮点型常量。用小数形式表示的浮点型常量必须要有小数点。
②指数形式表示:这种形式类似于数学中的指数形式。在数学中,浮点型常量可以用幂的形式来表示,如2.3026可以表示为0.23026×10^1、2.3026×10^0、23.026×10^-1等形式。在C51语言中,则以“e”或“E”后跟一个整数来表示以“10”为底的幂数。2.3026可以表示为0.23026E1、2.3026e0、23.026e-1。C51语言规定,字母e或E之前必须要有数字,且e或E后面的指数必须为整数,如e3、5e3.6、.e、e等都是非法的指数形式。在字母e或E的前后及数字之间不得插入空格。
3.字符型常量
字符型常量是用单引号括起来的单个普通字符或转义字符。
①普通字符常量:用单引号括起来的普通字符,如'b'、'xyz'、'?'等。字符型常量在计算机中是以其代码形式(一般采用ASCII代码)存储的。
②转义字符常量:用单引号括起来的前面带反斜杠的字符,如'\n'、'\xhh'等,其含义是将反斜杠后面的字符转换成其他含义。表1-8列出了一些常用的转义字符及含义。
表1-8 一些常用的转义字符及含义
4.符号型常量
在C51语言中,可以用一个标识符来表示一个常量,称为符号型常量。符号型常量在程序开头定义后,可以在程序中直接调用,在程序中其值不会更改。符号型常量在使用之前必须先定义,其一般形式为:
例如,在程序开头编写“#define PRICE 25”,就将PRICE定义为符号型常量,在程序中,PRICE就代表25。
变量是指程序运行时其值可以改变的量。每个变量都有一个变量名,变量名必须以字母或下画线“_”开头。在使用变量前需要先声明,以便程序在存储区域为该变量留出一定的空间。例如,在程序中编写“unsigned char num=123”,就声明了一个无符号字符型变量num,程序会在存储区域留出一个字节的存储空间,将该空间命名(变量名)为num,在该空间存储的数据(变量值)为123。
变量类型有位变量、字符型变量、整型变量和浮点型变量(也称实型变量)。
①位变量(bit):占用的存储空间为1位,位变量的值为0或1。
②字符型变量(char):占用的存储空间为1个字节(8位),无符号字符型变量的数值范围为0~255,有符号字符型变量的数值范围为-128~+127。
③整型变量:可分为短整型变量(int或short)和长整型变量(long),短整型变量的长度(即占用的存储空间)为2个字节,长整型变量的长度为4个字节。
④浮点型变量:可分为单精度浮点型变量(float)和双精度浮点型变量(double),单精度浮点型变量的长度(即占用的存储空间)为4个字节,双精度浮点型变量的长度为8个字节。由于浮点型变量会占用较多的空间,故编程时尽量少用浮点型变量。
C51变量的类型、长度和取值范围如表1-9所示。
表1-9 C51变量的类型、长度和取值范围
C51的运算符可分为算术运算符、关系运算符、逻辑运算符、位运算符和复合赋值运算符。
1.算术运算符
C51的算术运算符如表1-10所示。在进行算术运算时,按“先乘除模,后加减,括号最优先”的原则进行,即乘、除、模运算优先级相同,加、减优先级相同且最低,括号优先级最高,优先级相同的运算按先后顺序进行。
表1-10 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次方(2 3 ),2^表示2的平方(2 2 )。
2.关系运算符
C51的关系运算符如表1-11所示。<、>、<=和>=运算优先级高且相同,==、!=运算优先级低且相同,如“a>b!=c”相当于“(a>b)!=c”。
表1-11 C51的关系运算符
用关系运算符将两个表达式(可以是算术表达式、关系表达式、逻辑表达式或字符表达式)连接起来的式子称为关系表达式,关系表达式的运算结果为一个逻辑值,即真(1)或假(0)。
例如,a=4、b=3、c=1,则:
a>b的结果为真,即值为1;
b+c<a的结果为假,即值为0;
(a>b)==c的结果为真,即值为1;
d=a>b,d的值为1;
f=a>b>c,由于关系运算符的结合性为左结合,a>b的值为1,而1>c的值为0,所以f的值为0。
3.逻辑运算符
C51的逻辑运算符如表1-12所示。&&和||为双目运算符,要求有两个运算对象,!为单目运算符,只需要有一个运算对象。&&和||运算优先级低且相同,!运算优先级高。
表1-12 C51的逻辑运算符
与关系表达式一样,逻辑表达式的运算结果也为一个逻辑值,即真(1)或假(0)。
例如,a=4、b=5,则:
!a的结果为假,因为a=4为真(a值非0即为真),所以!a为假(0);
a||b的结果为真(1);
!a&&b的结果为假(0),因为!的优先级高于&&,故先运算!a,结果为0,0&&b的结果也为0。
在进行算术、关系、逻辑和赋值混合运算时,其优先级从高到低依次为:!(非)→算术运算符→关系运算符→&&和||→赋值运算符(=)。
4.位运算符
C51的位运算符如表1-13所示。位运算的数据类型必须是位型、整型或字符型,不能为浮点型。
表1-13 C51的位运算符
位运算举例如下:
5.复合赋值运算符
复合赋值运算符就是在赋值运算符“=”前面加上其他运算符,C51常用的复合赋值运算符如表1-14所示。
表1-14 C51常用的复合赋值运算符
复合赋值运算就是变量与表达式先按运算符运算,再将运算结果赋给参与运算的变量。凡是双目运算(两个对象运算)都可以用复合赋值运算符去简化表达。
复合赋值运算的一般形式为:
例如,a+=28相当于a=a+28。
在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等。这些关键字可分为以下几类:
①数据类型关键字,用来定义变量、函数或其他数据结构的类型,如unsigned char、int等。
②控制语句关键字,在程序中起控制作用的语句,如while、for、if、case等。
③预处理关键字,表示预处理命令的关键字,如define、include等。
④存储类型关键字,表示存储类型的关键字,如static、auto、extern等。
⑤其他关键字,如const、sizeof等。
2.由Keil C51编译器扩充的关键字
由Keil C51编译器扩充的关键字可分为以下两类:
①用于定义51单片机内部寄存器的关键字,如sfr、sbit。
sfr用于定义特殊功能寄存器,如“sfr P1=0x90;”是将地址为0x90的特殊功能寄存器名称定义为P1;sbit用于定义特殊功能寄存器中的某一位,如“sbit LED1=P1^1;”是将特殊功能寄存器P1的第1位名称定义为LED1。
②用于定义51单片机变量存储类型关键字。这些关键字有6个,如表1-15所示。
表1-15 用于定义51单片机变量存储类型关键字
数组也常称为表格,是指具有相同数据类型的数据集合。在定义数组时,程序会将一段连续的存储单元分配给数组,存储单元的最低地址存放数组的第一个元素,最高地址存放数组的最后一个元素。
根据维数不同,数组可分为一维数组、二维数组和多维数组;根据数据类型不同,数组可分为字符型数组、整型数组、浮点型数组和指针型数组。在用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。
在定义数组时,要注意以下几点:
①数组名应与变量名一样,必须遵循标识符命名规则,在同一个程序中,数组名不能与变量名相同。
②数组中的每个元素的数据类型必须相同,并且与数组类型一致。
③数组名后面的下标表示数组的元素个数(又称数组长度),必须用方括号括起来,下标是一个整型值,可以是常数或符号型常量,不能包含变量。
2.二维数组
(1)数组定义
二维数组的一般定义形式如下:
下标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)数组赋值
二维数组赋值有两种方法:
①按存储顺序赋值。例如:
unsigned int a[2][3]={1,16,3,0,28,255};
②按行分段赋值。例如:
unsigned int a[2][3]={{1,16,3},{0,28,255}};
3.字符型数组
字符型数组用来存储字符型数据。字符型数组可以在定义时进行初始化赋值。例如:
char c[4]={ 'A','B','C','D'};
以上定义了一个字符型数组,数组名为c,数组中存放4个字符型元素(占用了4个字节的存储空间),分别是A、B、C、D(实际上存放的是这4个字母的ASCII码,即0x65、0x66、0x67、0x68)。如果对全体元素赋值,则数组的长度(下标)可省略,即上述数组定义也可写成:
如果要在字符型数组中存放一个字符串"good",可采用以下三种方法:
如果要在二维字符型数组中存放多个字符串,则二维字符型数组的下标1为字符串的个数,下标2为每个字符串的长度。下标1可以不写,下标2必须写,并且其值至少较最长字符串的字符数(空格也算一个字符)多出一个。例如:
上例中的“\n”是一种转义符号,其含义是换行,用于将当前位置移到下一行开头。
在编程时,如果需要某段程序反复执行,则可使用循环语句。C51的循环语句有三种:while语句、do while语句和for语句。
1.while语句
while语句的格式为“while(表达式){语句组;}”,一般按以下方式编写:
while语句在执行时,先判断表达式是否为真(非0即为真)或表达式是否成立,若为真或表达式成立则执行大括号(也称花括号)内的语句组(也称循环体),否则不执行大括号内的语句组,直接跳出while语句,执行大括号之后的内容。
在使用while语句时,要注意以下几点:
①当while语句的大括号内只有一条语句时,可以省略大括号,但使用大括号可使程序更安全可靠。
②当while语句的大括号内无任何语句(空语句)时,应在大括号内写上分号“;”,即“while(表达式){;}”,简写就是“while(表达式);”。
③如果while语句的表达式是递增或递减表达式,则while语句每执行一次,表达式的值就增1或减1,如“while(i++){语句组;}”。
④如果希望某语句组无限次循环执行,则可使用“while(1){语句组;}”。如果希望程序停在某处等待,待条件(即表达式)满足时往下执行,则可使用“while(表达式);”。如果希望程序始终停在某处不往下执行,则可使用“while(1);”,即让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语句。
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成立则执行语句组2……最后判断表达式 n 是否为真或表达式是否成立,若为真或表达式 n 成立则执行语句组 n ,如果所有的表达式都不成立或为假,则跳出if…else if…语句。
2.switch…case语句
switch…case语句格式如下:
switch…case语句执行时,首先计算表达式的值,然后按顺序逐个与各case后面的常量表达式的值进行比较,当与某个常量表达式的值相等时,执行该常量表达式后面的语句组,并执行break,跳出switch…case语句,如果表达式与所有case后面的常量表达式的值都不相等,则执行default后面的语句组,并跳出switch…case语句。