Cx51流程控制共有3种基本结构:顺序结构、选择结构以及循环结构。
一、顺序结构
顺序结构是最基本、最简单的编程结构,程序按先后顺序执行指令代码。
下图是8051单片机的P0口和P1口上分别连接了8个LED,请分别用P0和P1口显示加法125+34和减法176-98的运算结果
#include
int main(void)
{
unsigned char a=125,b=34,c=176,d=98;
P1=a+b; //加法运算结果送P1端口,P1=159=1001 1111B
P0=c-d; //减法运算结果送P0端口,P0=78=0100 1110B
while(1); //循环等待,防止主程序退出后单片机跑飞
}
二、选择结构
2.选择结构
(1)if语句
if语句用于根据条件判定结果决定执行的语句。if语句有三种基本形式:
if(表达式)
{语句组}
说明:如果“表达式”为真,则执行花括号中的语句组,否则跳过花括号执行下面的语句。
if(表达式)
{语句组1}
else
{语句组2}
说明:如果“表达式”真,则执行语句组1,否则执行语句组2。
if(表达式1)
{语句1}
else if(表达式2)
{语句组2 }
else if(表达式3)
{语句组3}
else if(表达式m)
{语句组m}
else
{语句组n}
说明:如果“表达式1”为真,则执行“语句组1”,否则如果“表达式2”为真,则执行“语句组2”…,如果所有的表达式都不满足,则执行语句组n。
同图4-2,用if语句根据54/18的计算结果选择P0口8位LED的状态。
#include //包含单片机寄存器的头文件
int main(void)
{
unsigned char a=54,b=18;
if (a/b==1)P0=0xfe; //第一个LED亮
else if (a/b==2) P0=0xfd; //第二个LED亮
else if (a/b==3) P0=0xfb; //第三个LED亮
else if (a/b==4) P0=0xf7; //第四个LED亮
else if (a/b==5) P0=0xef; //第五个LED亮
else if (a/b==6) P0=0xdf; //第八个LED亮
else if (a/b==7) P0=0xbf; //第七个LED亮
else if (a/b==8) P0=0x7f; //第八个LED亮
else P0=0xff; //缺省值,关闭所有LED
while(1);
}
(2)switch/case语句
根据表达式的值决定要执行的语句组,用于实现多中选一,形式如下:
switch(表达式)
{
case常量表达式1://如果1满足,则执行语句组1
语句组1;
break; //跳出switch结构
case常量表达式2://如果2满足,则执行语句组2
语句组2;
break; //跳出switch结构
…
default: //条件都不满足时,执行语句组n
语句组n;
}
同图4-2,用swtich语句根据54/18的计算结果选择P0口8位LED的状态
#include //包含单片机寄存器的头文件
int main(void)
{
unsigned char a=56,b=18;
switch(a/b) //使用多分支选择语句
{
case 1: P0=0xfe; break; //第一个LED亮
case 2: P0=0xfd; break; //第二个LED亮
case 3: P0=0xfb; break; //第三个LED亮
case 4: P0=0xf7; break; //第四个LED亮
case 5: P0=0xef; break; //第五个LED亮
case 6:P0=0xdf; break; //第六个LED亮
case 7:P0=0xbf; break; //第七个LED亮
case 8:P0=0x7f; break; //第八个LED亮
default: P0=0xff; //缺省值,关闭所有LED
}
while(1);
}
1、for循环
for 循环结构用于按指定的次数循环执行一组语句,格式如下:
for(表达式1;表达式2;表达式3)
{语句;}
for循环语句执行过程如下:
(1)先执行表达式1,一般是对循环变量赋初值;
(2)执行表达式2,若表达式2结果为真,则执行循环体语句,并求解表达式
3(一般是改变循环变量的值);然后再次执行表达式2;并判断结果真假
(3)若表达式2结果为假,则退出for循环。
三、循环结构
while循环
while循环语句先判断条件真假,若表达式为真,则执行花括号内的语句组,否则终止循环,格式如下:
while(表达式)
{语句组}
同图4-3,用while循环计算7的阶乘并送P1和P0口显示结果。
#include //包含单片机寄存器的头文件
int main(void)
{
unsigned char i=1;
unsigned int s=1;
while(i<=7)
{
s=s*i;//计算阶乘
i++; //i自增运算
}
P1=s/256; //高8位送P1显示
P0=s%256; //低8位送P0显示
while(1);
}
do while 循环
先执行花括号内的语句组,然后执行表达式,若结果为真,则重复执行花括号内的语句组,否则终止循环,格式如下:
do
{语句组}
while(表达式);
根据下图用do while循环计算7的阶乘并送P1和P0口显示结果。
#include //包含单片机寄存器的头文件
int main(void)
{
unsigned char i=1;
unsigned int s=1;
do
{
s=s*i;//计算阶乘
i++; //i自增运算
} while(i<=7);
P1=s/256; //高8位送P1显示
P0=s%256; //低8位送P0显示
while(1);
}
四、数组
1.一维数组
(1)一维数组的定义方式为:
数据类型 数组名 [常量表达式];
例如定义有3个元素字符型数组m:
charm[3];
m的3个元素分别是m[0], m[1], m[2]。数组的类型指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。
(2)一维数组的初始化
数组初始化是指在数组定义时给数组元素赋予初值。
例如:
int m[3]={ 0,1,2};
相当于a[0]=0,a[1]=1,a[2]=2。
当{ }中值的个数少于元素个数时,只给前面部分元素赋值,其他元素自动赋0值。
例如:
int m[3]={0,1}
(3)一维数组元素的引用
数组必须预先定义才能使用。数组只能逐个地使用各元素,而不能一次引用整个数组。例如,输出有10个元素的数组必须使用循环语句逐个输出各下标变量:
for(i=0;i<3; i++)
printf("%d",m[i]);
而不能用一个语句输出整个数组。因此,下面的写法是错误的:
printf("%d",m);
用一维数组实现流水点亮P0口的8位LED
int main(void) //主函数
{
unsigned char i;
unsigned char code Tab[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //定义无符号字符型数组
while(1)
{
for(i=0;i<8;i++)
{
P0=Tab[i];//依次引用数组元素,并将其送P0口显示
delay();//调用延时函数
}
}
}
2.二维数组
二维数组是以行列矩阵的形式存储数据。
** (1)二维数组定义**
数据类型 数组名[下标1][下标2];
第一个下标代表行,第二个下标代表列。例如语句 int a[2][3] 定义了二维整形数组,共有2行3列6个元素。
(2)二维数组的初始化
可以采用以下两种方式初始化:
按先行后列的存储顺序整体赋值,例如:
int a[2][3]={0,1,2,3,4,5}
按每行分别赋值,例如:
int a[2][3]={{0,1,2},{3,4,5}};
用二维数组实现流水点亮P0口的8位LED。
int main(void)
{
unsigned char i,j;
unsigned char codeTab[2][4]={{0xfe,0xfd,0xfb,0xf7},{0xef,0xdf,0xbf,0x7f}}; //定义无符 //号字符型数组
while(1)
{
for(i=0;i<2;i++)
for(j=0;j<4;j++)
{
P0=Tab[i][j];//依次引用数组元素,并将其送P0口显示
delay();//调用延时函数
}
}
}
变量的指针就是变量的地址。在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。
指针变量是一种特殊的变量,它也和一般变量一样,具有变量名、类型和值,但它的值就是变量所存放的地址。
通过指针对变量进行操作是间接操作,比直接操作变量更费时间,且不够直观,但可以通过灵活运用指针使程序代码更为简洁、有效。
四、指针
1、指针变量的定义
指针变量同普通变量一样,使用之前要进行定义。指针变量定义的一般形式为:
数据类型 *变量名;
其中,数据类型表示本指针变量所指向变量的数据类型,*表示这是一个指针变量。
例如:定义:int*point,表示point指向一个整型变量,但point具体指向哪一个整型变量,取决于point中所存储的地址。一个指针变量只能指向同类型的变量,如上述point 只能指向整形变量,不能指向一个字符变量。
2、指针变量的引用
指针变量在使用之前必须赋予具体的地址,使用未经赋值的指针变量会引起严重后果。指针变量的引用有两个重要运算符:
&:取地址运算符;
*:指针运算符(或称“间接访问” 运算符)。
在指针变量定义中所出现的“ ”是类型说明符,表示其后的变量是指针类型,而指针运算符“ ”则出现在表达式中,用以表示指针指向的变量值。
地址运算符&用来取出变量的地址。其形式为:
&变量名;
Ø指针运算符*用来取出指针指向变量的值,其形式为:
*指针变量名;
inti,j;
int *point;
point= &i;
j=*point;
表示取出变量i的值赋予变量j,与语句j=i的效果相同。对*point的任何操作与直接对变量i的操作效果相同。
3、数组指针
一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。
一个数组是由连续的一块内存单元组成的。数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的。每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址。
定义一个指向数组元素的指针变量,与定义普通变量的指针相同。例如:
intm[]={1,2,3};4int*p;4p=&m[0];
经上述定义后,p就是数组m的指针。因为数组名代表数组的首地址,也就是第一个元素的地址,因此下面两个语句等价:
p=&m[0];
p=m;
p指向数组m的首地址后,p+i就是数组的元素m[i]。
例题:用一维数组实现流水点亮P0口的8位LED。
int main(void)
{
unsigned char i;
unsigned char code Tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; /*定义无符号字符型数组*/
unsigned char *p;
p=&Tab[0];/*定义指针变量并指向Tab数组,也可写为unsigned char *p=Tab */
while(1)
{
for(i=0;i<8;i++)
{
P0=*(p+i);//通过指针变量依次引用数组元素,并将其送P0口显示
delay();//调用延时函数
}
}
}
指向二维数组的指针变量的说明形式为
类型说明符 (*指针变量名)[长度];
“长度”是二维数组的列数。二维数组的每一行都代表一个一维数组,该一维数组的长度就是二维数组的列数。若该指针变量指向二组数组A[m][n]的首地址,则 (指针变量名+i)就是A[i],而A[i]是一维数组A[i][n]的首地址, (*(指针变量名+i)+j)就是A[i][j]的值。
例题:用二维数组实现流水点亮P0口的8位LED。
int main(void)
{
unsigned char i,j;
unsigned char codeTab[2][4]={{0xfe,0xfd,0xfb,0xf7},{0xef,0xdf,0xbf,0x7f}}; //定
//义无符号字符型数组
unsigned char (*p)[4]; //定义二维数组指针
p=Tab;//指向二维数组首地址
while(1)
{
for(i=0;i<2;i++)
for(j=0;j<4;j++)
{
P0=*(*(p+i)+j);//依次引用数组元素,并将其送P0口显示
delay();//调用延时函数
}
}
}