单片机与电路绘图自学手册
上QQ阅读APP看书,第一时间看更新

1.3 C51语言基础

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

1.3.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×101、2.3026×100、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'等,其含义是将反斜杠后面的字符转换成另外的含义。表1-2列出一些常用的转义字符及含义。

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

4. 符号常量

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

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

 #define 标识符 常量

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

1.3.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变量的类型、长度和取值范围见表1-3。

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

1.3.3 运算符

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

1. 算术运算符

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

表1-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的平方(22)。

2. 关系运算符

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

表1-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的逻辑运算符见表1-6。“&&”、“||”为双目运算符,要求两个运算对象,“!”为单目运算符,只需要有一个运算对象。&&、||运算优先级低且相同,!运算优先级高。

表1-6 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-7。位运算的对象必须是位型、整型或字符型数,不能为浮点型数。

表1-7 C51的位运算符

位运算举例如下:

5. 复合赋值运算符

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

表1-8 C51常用的复合赋值运算符

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

复合运算的一般形式为:

 变量 复合赋值运算符 表达式

例如:a+=28相当于a=a+28。

1.3.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个,见表1-9。

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

1.3.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。

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

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

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

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

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)数组赋值

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

按存储顺序赋值。如下所示。

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)。如果对全体元素赋值时,数组的长度(下标)也可省略,即上述数组定义也可写成如下所示。

 char c[]={ 'A', 'B', 'C', 'D'};

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

 char c[]={ 'g', 'o', 'o', 'd', '\0'}; //“\0”为字符串的结束符
 char c[]={"good"}; //使用双引号时,编译器会自动在后面加结束符'\0',故数组长度应较字符数多一个
 char c[]="good";

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

 char c[][20]={{"How old are you?",\n}, {"I am 18 years old.",\n},{"and you?" }};

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

1.3.6 循环语句(while、do while、for语句)

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

1. while语句

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(表达式)

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

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

3. for语句

for语句的格式如下。

 for(初始化表达式; 条件表达式; 增量表达式)
 {
 语句组;
 }

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

1.3.7 选择语句(if、switch…case语句)

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

1. if语句

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

(1)基本if语句

基本if语句格式如下。

 if(表达式)
 {
 语句组;
 }

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

(2)if…else…语句

if…else…语句格式如下。

yuan if(表达式)
 {
 语句组1;
 }
 else
 {
 语句组2;
 }

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

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

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

 if(表达式1)
 {
 语句组1;
 }
 else if(表达式2)
 {
 语句组2;
 }
 ……
 else if(表达式n)
 {
 语句组n;
 }

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

2. switch…case语句

switch…case语句格式如下。

 switch(表达式)
 {
 case 常量表达式1; 语句组1;break;
 case 常量表达式2; 语句组2;break;
 ……
 case 常量表达式n; 语句组n;break;
 default:语句组n+1;
 }

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