A. 数组
一.一维数组
一维数组只有一个下标,定义的形式如下:
数据类型说明符 数组名[常量表达式][={初值,初值……}]
各部分说明如下:
(1)“数据类型说明符”说明了数组中各个元素存储的数据的类型。
(2)“数组名”是整个数组的标识符,它的取名方法与变量的取名方法相同。
(3)“常量表达式”,常量表达式要求取值要为整型常量,必须用方括号“[]”括起来。用于说明该数组的长度,即该数组元素的个数。
(4)“初值部分”用于给数组元素赋初值,这部分在数组定义时属于可选项。对数组元素赋值,可以在定义时赋值,也可以定义之后赋值。在定义时赋值,后面须带等号,初值须用花括号括起来,括号内的初值两两之间用逗号间隔,可以对数组的全部元素赋值,也可以只对部分元素赋值。初值为0的元素可以只用逗号占位而不写初值0。
例如:下面是定义数组的两个例子。
unsigned char x[5];
unsigned int y[3]={1,2,3};
第一句定义了一个无符号字符数组,数组名为x,数组中的元素个数为5。
第二句定义了一个无符号整型数组,数组名为y,数组中元素个数为3,定义的同时给数组中的三个元素赋初值,赋初值分别为1、2、3。
需要注意的是,C51语言中数组的下标是从0开始的,因此上面第一句定义的5个元素分别是:x[0]、x[1]、x[2]、x[3]、x[4]。第二句定义的3个元素分别是:y[0]、y[1]、y[2]。赋值情况为:y[0]=1;y[1]=2;y[2]=3。
C51规定在引用数组时,只能逐个引用数组中的各个元素,而不能一次引用整个数组。但如果是字符数组则可以一次引用整个数组。
【例】用数组计算并输出Fibonacci数列的前20项。
Fibonacci数列在数学和计算机算法中十分有用。Fibonacci数列是这样的一组数:第一个数字为0,第二个数字为1,之后每一个数字都是前两个数字之和。设计时通过数组存放Fibonacci数列,从第三项开始可通过累加的方法计算得到。
程序如下:
#include <reg52.h> //包含特殊功能寄存器库
#include <stdio.h> //包含I/O函数库
extern serial_initial();
main()
{
int fib[20],i;
fib[0]=0;
fib[1]=1;
serial_initial();
for (i=2;i<20;i++) fib[i]=fib[i-2]+fib[i-1];
for (i=0;i<20;i++)
{
if (i%5= =0) printf(“\n”);
printf(“%6d”,fib[i]);
}
while(1);
}
程序执行结果:
0 1 1 2 3
5 8 13 21 34
55 89 144 233 377
610 987 1597 2584 4148
二.字符数组
用来存放字符数据的数组称为字符数组,它是C语言中常用的一种数组。字符数组中的每一个元素都用来存放一个字符,也可用字符数组来存放字符串。字符数组的定义下一般数组相同,只是在定义时把数据类型定义为char型。
例如:char string1[10];
char string2[20];
上面定义了两个字符数组,分别定义了10个元素和20个元素。
在C51语言中,字符数组用于存放一组字符或字符串,字符串以“\0”作为结束符,只存放一般字符的字符数组的赋值与使用和一般的数组完全相同。对于存放字符串的字符数组。既可以对字符数组的元素逐个进行访问,也可以对整个数组按字符串的方式进行处理。
【例】对字符数组进行输入和输出。
#include <reg52.h> //包含特殊功能寄存器库
#include <stdio.h> //包含I/O函数库
extern serial_initial();
main()
{
char string[20];
serial_initial();
printf(“please type any character:”);
scanf(“%s”,string);
printf(“%s\n”,string);
while(1);
}
B. 指针
指针是C语言中的一个重要概念。指针类型数据在C语言程序中使用十分普遍,正确地使用指针类型数据,可以有效地表示复杂的数据结构;可以动态地分配存储器,直接处理内存地址。
一.指针的概念
了解指针的基本概念,先要了解数据在内存中的存储和读取方法。
在汇编语言中,对内存单元数据的访问是通过指明内存单元的地址。访问时有两种方式:直接寻址方式和间接寻址方式。直接寻址是通过在指令中直接给出数据所在单元的地址而访问该单元的数据。例如:MOV A,20H。在指令中直接给出所访问的内存单元地址20H,访问的是地址为20H的单元的数据,该指令把地址为20H的片内RAM单元的内容送累加器A;间接寻址是指所操作的数据所在的内存单元地址不是通过指令中直接提供,该地址是存放在寄存器中或其它的内存单元中,指令中指明存放地址的寄存器或内存单元来访问相应的数据。
在C语言中,可以通过地址方式来访问内存单元的数据,但C语言作为一种高级程序设计语言,数据通常是以变量的形式进行存放和访问的。对于变量,在一个程序中定义了一个变量,编译器在编译时就在内存中给这个变量分配一定的字节单元进行存储。如对整型变量(int)分配2个字节单元,对于浮点型变量(float)分配4个字节单元,对于字符型变量分配1个字节单元等。变量在使用时分清两个概念:变量名和变量的值。前一个是数据的标识,后一个是数据的内容。变量名相当于内存单元的地址,变量的值相当于内存单元的内容。对于内存单元的数据访问方式有两种,对于变量也有两种访问方式:直接访问方式和间接访问方式。
直接访问方式。对于变量的访问,我们大多数时候是直接给出变量名。例如:printf(“%d”,a),直接给出变量a的变量名来输出变量a的内容。在执行时,根据变量名得到内存单元的地址,然后从内存单元中取出数据按指定的格式输出。这就是直接访问方式。
间接访问方式。例如要存取变量a中的值时,可以先将变量a的地址放在另一个变量b中,访问时先找到变量b,从变量b中取出变量a的地址,然后根据这个地址从内存单元中取出变量a的值。这就是间接访问。在这里,从变量b中取出的不是所访问的数据,而是访问的数据(变量a的值)的地址,这就是指针,变量b称为指针变量。
关于指针,注意两个基本概念:变量的指针和指向变量的指针变量。变量的指针就是变量的地址。对于变量a,如果它所对应的内存单元地址为2000H,它的指针就是2000H。指针变量是指一个专门用来存放另一个变量地址的变量,它的值是指针。上面变量b中存放的是变量a的地址,变量b中的值是变量a的指针,变量b就是一个指向变量a的指针变量。
如上所述,指针实质上就是各种数据在内存单元的地址,在C51语言中,不仅有指向一般类型变量的指针,还有指向各种组合类型变量的指针。在本书中我们只讨论指向一般变量的指针的定义与引用,对于指向组合类型的指针,大家可以参考其它书籍学习它的使用。
二.指针变量的定义
指针变量的定义与一般变量的定义类似,定义的一般形式为:
数据类型说明符 [存储器类型] *指针变量名;
其中:
“数据类型说明符”说明了该指针变量所指向的变量的类型。
“存储器类型”是可选项,它是C5l编译器的一种扩展.如果带有此选项.指针被定义为基于存储器的指针。无此选项时,被定义为一般指针,这两种指针的区别在于它们占的存储字节不同。
下面是几个指针变量定义的例子:
int * p1; /*定义一个指向整型变量的指针变量p1*/
char * p2; /*定义一个指向字符变量的指针变量p2*/
char data * p3; /*定义一个指向字符变量的指针变量p3,该指针访问的数据在片内数据存储器中,该指针在内存中占一个字节*/
float xdata * p4; /*定义一个指向字符变量的指针变量p4,该指针访问的数据在片外数据存储器中,该指针在内存中占两个字节*/
三.指针变量的引用
指针变量是存放另一变量地址的特殊变量,指针变量只能存放地址。指针变量使用时注意两个运算符:&和*。这两个运算符在前面已经介绍,其中:“&”是取地址运算符,“*”是指针运算符。通过“&”取地址运算符可以把一个变量的地址送给指针变量,使指针变量指向该变量;通过“*”指针运算符可以实现通过指针变量访问它所指向的变量的值。
指针变量经过定义之后可以象其他基本类型变量一样引用。例如:
int x,* px,* py; /*变量及指针变量定义*/
px=&x; /*将变量x的地址赋给指针变量px,使px指向变量x*/
* px=5; /*等价于x=5*/
py=px; /*将指针变量px中的地址赋给指针变量py,使指针变量py也指向x*/
【例】输入两个整数x与y,经比较后按大小顺序输出。
程序如下:
#include <reg52.h> //包含特殊功能寄存器库
#include <stdio.h> //包含I/O函数库
extern serial_initial();
main()
{
int x,y;
int * p,* p1,* p2;
serial_initial();
printf(“input x and y:\n”);
scanf(“%d%d”,&x,&y);
p1=&x;p2=&y;
if (x<y) {p=p1;p1=p2;p2=p;}
printf(“max=%d,min=%d\n”,*p1,*p2);
while(1);
}
程序执行结果:
input x and y:
4 8
max=8,min=4
C. 结构
结构是一种组合数据类型,它是将若干个不同类型的变量结合在一起而形成的一种数据的集合体。组成该集合体的各个变量称为结构元素或成员。整个集合体使用一个单独的结构变量名。
一.结构与结构变量的定义
结构与结构变量是两个不同的概念,结构是一种组合数据类型,结构变量是取值为结构这种组合数据类型的变量,相当于整型数据类型与整型变量的关系。对于结构与结构变量的定义有两种方法。
1.先定义结构类型再定义结构变量
结构的定义形式如下:
struct 结构名
{结构元素表};
结构变量的定义如下:
struct 结构名 结构变量名1,结构变量名2,……;
其中,“结构元素表”为结构中的各个成员,它可以由不同的数据类型组成。在定义时须指明各个成员的数据类型。
例如,定义一个日期结构类型date,它由三个结构元素year、month、day组成,定义结构变量d1和d2,定义如下:
struct date
{
int year;
char month,day;
}
struct date d1,d2;
2.定义结构类型的同时定义结构变量名
这种方法是将两个步骤合在一起,格式如下:
struct 结构名
{结构元素表} 结构变量名1,结构变量名2,……;
例如对于上面的日期结构变量d1和d2可以按以下格式定义:
struct date
{
int year;
char month,day;
}d1,d2;
对于结构的定义说明如下:
(1)结构中的成员可以是基本数据类型,也可以是指针或数组,还可以是另一结构类型变量,形成结构的结构,即结构的嵌套。结构的嵌套可以是多层次的,但这种嵌套不能包含其自己。
(2)定义的一个结构是一个相对独立的集合体,结构中的元素只在该结构中起作用,因而一个结构中的结构元素的名字可以与程序中的其它变量的名称相同,它们两者代表不同的对象,在使用时互相不影响。
(3)结构变量在定义时也可以像其它变量在定义时加各种修饰符对它进行说明。
(4)在C51中允许将具有相同结构类型的一组结构变量定义成结构数组,定义时与一般数组的定义相同,结构数组与一般变量数组的不同就在于结构数组的每一个元素都是具有同一结构的结构变量。
二.结构变量的引用
结构元素的引用一般格式如下:
结构变量名.结构元素名
或
结构变量名->结构元素名
其中,“.”是结构的成员运算符,例如:d1.year表示结构变量d1中的元素year,d2.day表示结构变量d2中的元素day等。如果一个结构变量中结构元素又是另一个结构变量,即结构的嵌套,则需要用到若干个成员运算符,一级一级找到最低一级的结构元素,而且只能对这个最低级的结构元素进行引用,形如d1.time.hour的形式。
【例】输入3个学生的语文、数学、英语的成绩,分别统计他们的总成绩并输出。
程序如下:
#include <reg52.h> //包含特殊功能寄存器库
#include <stdio.h> //包含I/O函数库
extern serial_initial();
struct student
{
unsigned char name[10];
unsigned int chinese;
unsigned int math;
unsigned int english;
unsigned int total;
}p1[3];
main()
{
unsigned char i;
serial_initial();
printf(“input 3 studend name and result:\n”);
for (i=0;i<3;i++)
{
printf(“input name:\n”);
scanf(“%s”,p1[i].name);
printf(“input result:\n”);
scanf(“%d,%d,%d”,&p1[i].chinese,&p1[i].math,&p1[i].english);
}
for (i=0;i<3;i++)
{
p1[i].total=p1[i].chinese+p1[i].math+p1[i].english;
}
for (i=0;i<3;i++)
{
printf(“%s total is %d”,p1[i].name,p1[i].total);
printf(“\n”);
}
while(1);
}
程序执行结果:
input 3 studend name and result:
input name:
wang
input result:
76,87,69
input name:
yang
input result:
75,77,89
input name:
zhang
input result:
72,81,79
wang total is 232
yang total is 241
zhang total is 232
D. 联合
前面介绍的结构能够把不同类型的数据组合在一起使用,另外,在C51语言中,还提供一种组合类型--联合,也能够把不同类型的数据组合在一起使用,但它与结构又不一样,结构中定义的各个变量在内存中占用不同的内存单元,在位置上是分开的,而联合中定义的各个变量在内存中都是从同一个地址开始存放,即采用了所谓的“覆盖技术”。这种技术可使不同的变量分时使用同一内存空间,提高内存的利用效率。
一.联合的定义
1.先定义联合类型再定义联合变量
定义联合类型,格式如下:
union 联合类型名
{成员列表};
定义联合变量,格式如下:
union 联合类型名 变量列表;
例如:
union data
{
float i;
int j;
char k;
}
union data a,b,c;
2.定义联合类型的同时定义联合变量
格式如下:
union 联合类型名
{成员列表}变量列表;
例如:
union data
{
float i;
int j;
char k;
}data a,b,c;
可以看出,定义时,结构与联合的区别只是将关键字由struct换成union,但在内存的分配上两者完全不同。结构变量占用的内存长度是其中各个元素所占用的内存长度的总和;而联合变量所占用的内存长度是其中各元素的长度的最大值。结构变量中的各个元素可以同时进行访问,联合变量中的各个元素在一个时刻只能对一个进行访问。
二.联合变量的引用
联合变量中元素的引用与结构变量中元素的引用格式相同,形式如下:
联合变量名.联合元素
或
联合变量名->联合元素
例如:对于前面定义的联合变量a、b、c中的元素可以通过下面形式引用。
a.i;
b.j;
c.k;
分别引用联合变量a中的float型元素i,联合变量b中的int型元素j,联合变量c中的char型元素k。
E. 枚举
枚举数据类型是一个有名字的某些整型常量的集合。这些整型常量是该类型变量可取的所有的合法值。枚举定义时应当列出该类型变量的所有可取值。
枚举定义的格式与结构和联合基本相同,也有两种方法。
先定义枚举类型,再定义枚举变量,格式如下:
enum 枚举名 {枚举值列表};
enum 枚举名 枚举变量列表;
或在定义枚举类型的同时定义枚举变量,格式如下:
enum 枚举名 {枚举值列表}枚举变量列表;
例如:定义一个取值为星期几的枚举变量d1。
enum week {Sun,Mon,Tue,Wed,Thu,Fri,Sat};
enum week d1;
或
enum week {Sun,Mon,Tue,Wed,Thu,Fri,Sat} d1;
以后就可以把枚举值列表中各个值赋值给枚举变量d1进行使用了。