打印
[51单片机]

从单片机基础到程序框架(连载)

[复制链接]
楼主: jianhong_wu
手机看帖
扫描二维码
随时随地手机跟帖
201
楼主加油。。。希望更新的速度快一些。。。。

使用特权

评论回复
202
jianhong_wu|  楼主 | 2016-11-20 11:13 | 只看该作者
本帖最后由 jianhong_wu 于 2016-11-20 11:18 编辑

第四十五节: 组合判断if...else if...else。
第四十五节_pdf文件.pdf (74.55 KB)
【45.1   三类组合判断语句的概述。】

       if的英文含义是“如果”,else是“否则”,else if是“否则如果”。在C语言里,if,else if ,else 所表达的含义,跟英文也是一样的。
       if,else if,else可以组成三种组合判断语句,第一种是“二选一”,第二种是“多选一”,第三种是“多选一或者什么都不选”。
       这类组合判断语句可以这样解读:在众多条件判断中,先从第一个if条件开始判断,如果第一个if条件是真,那么不管后面的条件是否为真,都不再判断,直接执行第一个if条件后面大括号的语句,组合语句中其它剩余的条件不再判断直接跳过,否则,就挨个条件往下判断,只要其中一个条件满足,就不再判断剩余的条件,也就是我们日常所说的多选一,甚至在某些组合语句如果所有条件都不满足,那么什么也不选。总之,在如此众多的条件中,最多只能执行一个条件后面大括号的语句。组合语句还有一个规律:if语句只能出现在第一个条件判断,而且只能出现一次;else只能出现在最后,而且也只能出现一次;而else if语句总是出现在中间,绝对不能出现在第一个条件判断,如果没有else,也可以出现在最后的条件判断。多说一句,在上述所提到的“只能出现一次”的概念仅仅局限于在一个组合判断语句的范围内,而组合判断语句在整个程序的出现次数是不受限制的。

【45.2   二选一的组合判断。】

        先讲第一种的“二选一”的书写格式,如下:
        书写格式如下:
if(条件1)   //if只能出现第一个条件,并且只能出现一次
{
    语句1;
}
else        //else只能出现最后,并且也只能出现一次。
{
       语句2;
}
语句3;

        这类语句的书写特点是:第一个是if判断语句,最后一个是else语句,中间没有else if判断语句。
        这类语句的执行顺序是:先判断第一个的if里面的(条件1),如果(条件1)满足而为真,就执行该(条件1)后面紧跟的大括号里面的“语句1”,执行完该大括号内的所有语句之后,就直接跳出整个组合判断的语句,不再判断也不再执行剩下来的else那部分的代码,直接跳到“语句3”处,从“语句3”处(包括“语句3”)继续往下执行。但是,如果第一个的if里面的(条件1)不满足而为假,那么就直接执行else后面大括号内的语句。也就是说,else是在if条件不满足时才执行的,所以叫“二选一”,在if和else之间二选一。

【45.3   多选一的组合判断。】

        接着讲第二种书写格式的“多选一”,这种书写格式,跟第一种对比,是在if与else的中间多插入了N个else if的判断语句。书写格式如下:
if(条件1)         //if只能出现第一个条件,并且只能出现一次
{
    语句1;
}
else if(条件2)   //else if只能出现中间或最后,可以出现多次
{
    语句2;
}
...
else if(条件N)   //else if只能出现中间或最后,可以出现多次
{
    语句N;
}
else             //else只能出现最后,并且也只能出现一次。
{
       语句N+1;
}
语句N+2;

        这类语句的书写特点是:第一行是if开始,最后一行以else结束,中间是N个else if判断语句。
        这类语句的执行顺序是:跟第一种“二选一”对比,判断顺序和规律大致也是一样的,也是从第一个if开始,往下逐个判断,然后到中间的else if,只要发现一个条件满足,就执行该条件后面的大括号内的代码,之后就马上结束整个组合判断语句,不再判断剩下的组合判断语句。但是,如果万一前面第一个if和中间所有的else if的条件都不满足而为假,就直接执行最后一个else大括号内的语句。所以叫“多选一”,在“第一个if、中间的else if、最后一个else”之间多选一。

【45.4   多选一或者什么都不选的组合判断。】

        最后讲第三种书写格式的“多选一或者什么都不选”,这种书写格式,跟第二种对比,只有第一个if和其它的else if语句,没有最后那个else语句。书写格式如下:
if(条件1)          //if只能出现第一个条件,并且只能出现一次
{
    语句1;
}
else if(条件2)    //else if只能出现中间或最后,可以出现多次
{
    语句2;
}
...
else if(条件N)   //else if只能出现中间或最后,可以出现多次
{
    语句N;
}
语句N+1;

        这类语句的书写特点是:第一行是if开始,中间是N个else if判断语句,没有最后一个else语句。
        这类语句的执行顺序是:跟第二种“多选一”对比,判断顺序和规律大致也是一样的,也是从第一个if开始,往下逐个判断,然后到中间的else if,只要发现一个条件满足,就执行该条件后面的大括号内的代码,之后就马上结束整个组合判断语句,不再判断剩余的组合判断语句。但是,如果万一前面第一个if和中间所有的else if的条件都不满足而为假,因为此时没有else语句,就意味着整个组合判断语句都没有条件满足,因此就没有相关满足的代码被执行到。所以把这种情况称为“多选一或者什么都不选”。

【45.5   例程练习和分析。】

        现在编写一个实验程序。
        程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

      //x这个变量作为条件判断的变量
      unsigned char x=5;

      //a,b,c这3个变量作为输出判断结果,0代表什么语句都没执行,1代表执行了语句1,
      //2代表执行语句2,3代表执行语句3。
      unsigned char a=0;  
      unsigned char b=0;
      unsigned char c=0;  

void main() //主函数
{

  //第一种“二选一”
  if(x>6)  
  {
     a=1;   //1代表执行了“语句1”
  }
  else
  {
     a=2;  //2代表执行了“语句2”
  }

  //第二种“多选一”
  if(x>6)
  {
     b=1;   //1代表执行了“语句1”
  }
  else if(7==x)  
  {
     b=2;  //2代表执行了“语句2”
  }
  else
  {
     b=3;  //3代表执行了“语句3”
  }

  //第三种“多选一或者什么都不选”
  if(x>6)  
  {
     c=1;   //1代表执行了“语句1”
  }
  else if(7==x)  
  {
     c=2;  //2代表执行了“语句2”
  }
  else if(8==x)  
  {
     c=3;  //3代表执行了“语句3”
  }  

     View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
     View(b);  //把第2个数b发送到电脑端的串口助手软件上观察。
     View(c);  //把第3个数c发送到电脑端的串口助手软件上观察。

     while(1)  
     {
     }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


        在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:2
十六进制:2
二进制:10

第2个数
十进制:3
十六进制:3
二进制:11

第3个数
十进制:0
十六进制:0
二进制:0


分析:        
       变量a为2。(2代表执行了语句2)
       变量b为3。(3代表执行了语句3)
       变量c为0。(0代表什么语句都没执行)


【45.6   如何在单片机上练习本章节C语言程序?】

        直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
203
jianhong_wu|  楼主 | 2016-11-27 11:51 | 只看该作者
本帖最后由 jianhong_wu 于 2016-12-4 08:54 编辑

第四十六节: 一维数组。
第四十六节_pdf文件.pdf (90.96 KB)
【46.1   数组是什么?】

       数组就是一堆变量或常量的集合。把一个数组里面某一个变量或者常量称为数组的元素,反过来也可以这么说,元素的集合就是数组。数组的最大特点就是内部所有的元素的地址都是挨家挨户相连的,同花顺似的,以第一个元素(下标是0的元素)为首地址,后来元素的地址挨个依次增大。首地址在RAM中的绝对地址往往是编译器自动分配的,我们不用管,可以看成是随机的。多说一句,在某些单片机,也可以通过特定的C语言关键词,强制要求编译器按我们的意愿,来分配到RAM中指定的某个绝对地址,这部分的内容这里暂时不讲。继续刚才的话题,首地址就像是一个坐标原点,一旦被编译器确定下来它在RAM中的地址,那么后面其它元素的地址都是在此基础上依次增大的,有规律的。正因为这个特点,数组在项目中往往起到缓存的作用。比如,在通信的项目中,用来作为一串数据的接收缓存。在界面显示的项目中,某个16x16点阵汉字的字模,需要一个内含32个元素的数组来作为缓存。在读写文件的项目中,也需要一个大数组来作为文件内容的缓存。在某些涉及复杂算法的项目,以数组作为缓存,并且通过配合循环语句或者指针,就可以快速批量的处理数据(循环语句和指针的相关知识后面章节会讲到)。总之,在项目应用中,数组无处不在。
       数组分为一维数组,二维数组,三维数组。一维数组应用最广,二维数组其次,三维数组最少用。所以本教程只讲一维数组和二维数组,本节先讲一维数组。

【46.2   一维数组的书写格式和特点。】

      一维数组不带初始化时候的定义格式如下:
      数据类型 数组名[数组元素总数N];

      数据类型是指unsigned char,unsigned int,unsigned long这类关键词;数组名就是由字母和数字组合而成的字符串,遵循常用变量的命名规则;N是数字,代表此数组内部有多少个元素。比如:
      unsigned char x[3];  //这里的3是数组内部元素的总数,但不是下标。

      上述这一行代码,就相当于一条语句定义了3个变量,这3个变量分别是x[0],x[1],x[2],但是不存在x[3]这个变量。这里,具体元素中括号内的“0,1,2”称为数组的下标,代表某个具体的元素。由此可见,数组有“批量定义”的特点。同时也要发现,此数组定义的N是3,代表内含3个元素变量,但是具体到某个元素的时候,下标不是从1开始,而是从0开始,最后一个也不是3而是2。可以这样描述,某个数组有N个元素,它具体元素的下标是从0开始,到N-1结束。那么问题来,如果一个数组明明最大只有N个元素,但是我在操作某个具体的元素时,非要用下标N或者N+1,也就是说,如果超过数组的范围的操作,会出现什么问题?后果严重吗?答案是:会导致数组越界出现异常或者编译不通过,可能会破坏其它数据,后果是严重的。因此大家使用数组的时候,要注意数组不能越界的问题。

      刚刚讲了一维数组不带初始化的定义格式,现在接着讲带初始化的定义格式,如下:
      数据类型 数组名[数组元素总数N]={元素0, 元素1,…元素N-1};

      比如:
      unsigned char y[3]={10,11,12};

      此数组一行代码定义了3个变量,分别是y[0], y[1], y[2]。而y[0]初始化为10,y[1]初始化为11,y[2]初始化为12。
      在程序中,操作数组某个变量元素时,下标可以是常量,比如y[0],此时的0就是常量;下标也可以是变量,比如y的中括号内的i,此时的i就是变量。再强调一次,作为下标的常量或者变量i的数值必须小于数组定义时的元素个数,否则就会导致数组越界出现异常或者编译不通过。

中括号内的N什么时候是“数组的元素总数”,什么时候是“数组的元素下标”,这个问题对初学者很容易混淆。其实很简单,定义的时候是“数组的元素总数”,操作调用具体某个元素的时候是“数组的元素下标”。

【46.3   什么情况下可以省略定义的元素总数?】

       一维数组在定义时,如果预先给它填写若干个初始化的数据,在语法上,也可以省略中括号[N]里面的元素总数N,这样编译器在编译时会根据初始化的总数来自动识别和定义此一维数组实际元素总数,分配对应数量的内存RAM。比如:

       unsigned char  y[3]={10,11,12};  //没有省略元素总数的写法


       跟

       unsigned char  y[]={10,11,12};   //在初始化的情况下,省略了元素总数的写法。


       的意义是一样的,都是合法的,都是C语言所允许的。注意,省略元素个数时必须要有初始化的数据,否则,编译器不知道此数组的长度,可能导致编译出错。

       这个功能在实际应用中有什么作用呢?在实际应用中,此项功能一般会用在常量数组里,而不是变量的数组里。当在数组定义的前面加上“const”或者“code”(针对51单片机)的关键词时,原来“变量”的数组就会变成“常量”的数组,这时,如果把常量的数组用来作为某个转换表格,此功能就很实用。因为作为转换表格的常量数组,我们在编程程序的过程中,有可能随时往里面添加数组,这个时候,不用我们刻意去计算和调整数组的元素总数N,给我们写程序带来了便利。对于这个功能的应用,大家先有一个感性的认识即可,暂时不用深入去了解,因为后续的章节还会讲解这方面的内容。

【46.4   例程练习和分析。】

    现在编写一个程序来熟悉一下一维数组的使用。
    程序代码如下:
/*---C语言学习区域的开始。-----------------------------------------------*/

     unsigned char  x[3];  //此处的3不是下标,而是元素总数,里面的3个变量没有初始化
     unsigned char  y[3]={10,11,12}; //里面三个元素变量y[0],y[1],y[2]分别初始化为10,11,12
     unsigned char  i=0; //定义和初始化一个变量。用来做x数组的下标。

void main() //主函数
{

x[i]=25;  //此时下标i为0.相当于把25赋值给x[0]
i=i+1;    //i由0变成1.
x[i]=26;  //此时下标i为1.相当于把26赋值给x[1]
i=i+1;    //i由1变成2.
x[i]=27;  //此时下标i为2.相当于把27赋值给x[2]
x[i]=x[i]+1; //此时x[2]自加1变成了28

     View(x[0]);  //把第1个数x[0]发送到电脑端的串口助手软件上观察。
     View(x[1]);  //把第2个数x[1]发送到电脑端的串口助手软件上观察。
     View(x[2]);  //把第3个数x[2]发送到电脑端的串口助手软件上观察。
     View(y[0]);  //把第4个数y[0]发送到电脑端的串口助手软件上观察。
     View(y[1]);  //把第5个数y[1]发送到电脑端的串口助手软件上观察。
     View(y[2]);  //把第6个数y[2]发送到电脑端的串口助手软件上观察。

     while(1)  
     {
     }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


   在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:25
十六进制:19
二进制:11001

第2个数
十进制:26
十六进制:1A
二进制:11010

第3个数
十进制:28
十六进制:1C
二进制:11100

第4个数
十进制:10
十六进制:A
二进制:1010

第5个数
十进制:11
十六进制:B
二进制:1011

第6个数
十进制:12
十六进制:C
二进制:1100


分析:        
       变量元素x[0]为25。
       变量元素x[1]为26。
       变量元素x[2]为28。
       变量元素y[0]为10。
       变量元素y[1]为11。
       变量元素y[2]为12。

【46.5   如何在单片机上练习本章节C语言程序?】

       直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
评论
zhangtao3b608 2021-5-15 16:00 回复TA
谢谢!楼主 
204
王区区| | 2016-12-1 10:26 | 只看该作者
你怎么这么牛X,我感觉好棒

使用特权

评论回复
205
1912740166| | 2016-12-1 20:33 | 只看该作者
jianhong_wu 发表于 2016-1-31 21:11
第三节:单片机最重要的一个特性。



楼主,看单片机汇编语言项目,可以参考哪些书籍和资料,希望解惑,万分感谢!QQ:1912740166

使用特权

评论回复
206
jianhong_wu|  楼主 | 2016-12-4 09:55 | 只看该作者
第四十七节: 二维数组。
第四十七节_pdf文件.pdf (74.94 KB)
【47.1   二维数组的书写格式和特点。】

       拿一维数组和二维数组来对比一下,一维数组只有一个下标,像由很多点连成的一条直线,整体给人的是一种“线”的观感。而二维数组有两个下标,这两个下标类似平面中的行与列,也类似平面中的X轴和Y轴的坐标,通过y轴和x轴坐标就可以找到所需的点,也就是二维数组的某个元素,因此,二维数组整体给人的是一种“面”的观感。

      上述是对二维数组的感性描述,二维数组是由一维数组发展而来,所以继承了很多一维数组的特点。二维数组的所有“网点”元素的地址都是挨个相临的,先从第0行开始“扫描”当前行的列,第0行第0列,第0行第1列,第0行第2列......再第1行第0列,第1行第1列,第1行第2列......再第2行......再第N行,上一行“尾”元素跟下一行“头”元素的地址也是相临连续的。
       二维数组未带初始化时的通用定义格式如下:
       数据类型 数组名[行数Y][列数X];

       比如:
       unsigned char  a[2][3]; //此处的2代表有2行,3代表有3列。

       分析:此二维数组定义了6个变量,跟一维数组一样,下标都是从0开始,到(N-1)时结束,此处的N代表行数或者列数。所以a[2][3]数组的元素挨个分别是a[0][0],a[0][1], a[0][2], a[1][0], a[1][1], a[1][2]这6个变量。这6个变量的地址是顺序挨个相连的。

       二维数组有两种常用初始化格式,一种是逐行初始化,一种是整体初始化。

       第一种逐行初始化:
unsigned char a[2][3]=
{
   {0,1,2},
   {3,4,5}
};

       在逐行初始化定义二维数组时,只要有初始化的数据,也可以省略行下标,但是列下标不能省略,比如:
unsigned char a[][3]=
{
   {0,1,2},
   {3,4,5}
};

       此时编译器会根据元素的个数来确定行数是多少。


       第二种整体初始化,跟一维数组一样,内部数据元素不需要额外增加大括号来分行。
unsigned char a[2][3]=
{
   0,1,2,3,4,5
};

       或者
unsigned char a[2][3]=
{
   0,1,2,
   3,4,5
};

都行。

       C语言是很丰富的语言,比如二维数组还允许不完全初始化的一些情况,这种情况我就不再深入讲解,我讲解的都是挑选一些针对以后单片机项目中可能会经常用到的语法。
       二维数组我在很多项目上还是经常用到的,比如用在一些需要把所得的信息进行查表判断的项目,在每一行里放一条关键词字符串信息,利用循环语句进行逐行查找匹配信息。至于二维数组如何存放字符串的知识点以后再讲。这节的重点是让大家对二维数组有个初步的认识。

【47.2   例程练习和分析。】

       现在编写一个程序来熟悉一下二维数组的书写和使用格式。
       程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

    unsigned char  a[2][3]=  //定义和初始化一个二维数组
    {
       {0,1,2},
       {3,4,5}
    };
void main() //主函数
{
     a[0][0]=8;  //故意把第0行第0列的这个变量赋值8,让大家看看如何直接操作二维数组某个元素。

     View(a[0][0]);  //把第1个数a[0][0]发送到电脑端的串口助手软件上观察。
     View(a[0][1]);  //把第2个数a[0][1]发送到电脑端的串口助手软件上观察。
     View(a[0][2]);  //把第3个数a[0][2]发送到电脑端的串口助手软件上观察。
     View(a[1][0]);  //把第4个数a[1][0]发送到电脑端的串口助手软件上观察。
     View(a[1][1]);  //把第5个数a[1][1]发送到电脑端的串口助手软件上观察。
     View(a[1][2]);  //把第6个数a[1][2]发送到电脑端的串口助手软件上观察。

     while(1)  
     {
     }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


       在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:8
十六进制:8
二进制:1000

第2个数
十进制:1
十六进制:1
二进制:1

第3个数
十进制:2
十六进制:2
二进制:10

第4个数
十进制:3
十六进制:3
二进制:11

第5个数
十进制:4
十六进制:4
二进制:100

第6个数
十进制:5
十六进制:5
二进制:101


分析:        
        变量元素a[0][0]为8。从原来定义的0变成8,因为被main函数里的第1行代码赋值了8。
        变量元素a[0][1]为1。
        变量元素a[0][2]为2。
        变量元素a[1][0]为3。
        变量元素a[1][1]为4。
        变量元素a[1][2]为5。

【47.3   如何在单片机上练习本章节C语言程序?】

        直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
207
西街耳呦| | 2016-12-5 14:50 | 只看该作者
本帖最后由 西街耳呦 于 2016-12-5 15:00 编辑
jianhong_wu 发表于 2016-4-3 10:38
第十二节:变量的定义和赋值。
【12.1   学习C语言的建议和方法。】
       先提一些学C语言的建议和方法, ...


使用特权

评论回复
208
wyb668| | 2016-12-6 00:08 | 只看该作者
吴老师新开帖了,支持你!还在学习你的  “[51单片机] 从业将近十年!手把手教你单片机程序框架(连载)”

使用特权

评论回复
209
jianhong_wu|  楼主 | 2016-12-11 10:59 | 只看该作者
第四十八节: while循环语句。
第四十八节_pdf文件.pdf (94.91 KB)
【48.1   程序的“跑道”。】

       经常听到这句话“程序跑起来了吗?”,程序在哪里跑?有跑道吗?有的。循环语句就像一条椭圆的跑道,程序在跑道上不停的跑,不知疲倦的跑,永无止境,一秒钟几百万圈的速度。单片机的main主函数内往往有一条while(1)语句,这就是单片机的“循环跑道”,称之为主循环,主循环内还可以继续嵌套多层while循环语句。

【48.2   while循环的常见格式。】

       常见格式如下:
while(条件)
{
    语句1;   
    语句2;
     ……   
    语句N;
}
语句N+1;



       上面的“花括号内”称为“循环体内”,“花括号外”称为“循环体外”,现在来分析一下上述代码的执行规律,如下:
       (1)像if语句一样,先判断while的(条件)是否为真。如果为“假”,就不执行循环体“内”的“语句1”至“语句N”,直接跳到循环体“外”的“语句N+1”处开始往下执行。如果为“真”,才执行循环体“内”的“语句1”至“语句N”,当执行完循环体“内”最后的“语句N“时,单片机会突然返回到第一行代码“while(条件)”处,继续判断循环的(条件)是否为真,如果为假就跳到循环体“外”的“语句N+1”,表示结束了当前循环。如果为真就继续从“语句1“执行到“语句N“,然后再返回while(条件)处,依次循环下去,直到条件为假时才罢休,否则一直循环下去。
      (2)while(条件)语句中,条件判断真假的规则跟if语句一模一样,有两种类型:一种是纯常量或者变量类型的,只要此数值不等于0就认为是真,所以while(1)也称死循环语句,因为里面的条件永远不为0。对于死循环这样的语句,如果不遇到break,return,goto这些语句,那么就永远也别想跳出这个循环;另外一种是关系判断,以及关系语句之间的像“与或”关系这类的判断。这些条件判断的真假,跟if语句的规则是一样的,这里不再多讲。break,return,goto这些语句后面章节会讲到。

【48.3   while省略花括号,没带分号。】

while(条件)
    语句1;   
    语句2;
    ……   
    语句N;
    语句N+1;

       上面的代码,居然没有了花括号,问题来了,此循环语句的“有效射程”究竟是多远,或者说,此循环语句的循环区域在哪里。现在跟大家解开这个谜团。第一行代码,while(条件)后面“没有分号”,接着第二行就是“语句1”,所以,这种情况跟if语句省略花括号的写法是一样的,此时循环体默认只包含离它最近的一条且仅仅一条的“语句1”,因此,上述的语句,等效于下面这种添加花括号的写法:  
while(条件)
{
    语句1;   
}
语句2;
……   
语句N;
语句N+1;


【48.4   while省略花括号,带分号。】

while(条件);
    语句1;   
    语句2;
    ……   
    语句N;
    语句N+1;

        这次的代码跟刚才“48.3”的代码唯一的差别是,第一行代码,while(条件)后面“有分号”。所以它循环的有效范围就在第一行就结束了,不涉及“语句1”。此时,等效于下面这种添加花括号的写法:   
while(条件)
{
   ;     //这里的分号代表一条空语句
}
语句1;   
语句2;
……   
语句N;
语句N+1;

        如果while的(条件)一直为“真”,单片机就一直在循环体内执行一条“无意义”的空语句,相当于“耗着”的状态,执行不到后面“语句1”的语句,除非,条件为“假”才罢休才会跳出循环体。
        循环体内什么都没有,只写一条“空语句”,这种写法在实际项目中也是有用武之地的,比如,等待某件事是否满足条件,如果不满足,就一直死等死磕在这里,其它事情都干不了,这种“死等死磕”的做法,专业术语叫“阻塞”,与之反面相对应的是另外一个词叫“非阻塞”。对于循环的“阻塞”用法,老练的工程师通常会多加一个超时的判断,这些内容大家暂时不用深入了解,后续章节我会讲到。

【48.5   例程练习和分析。】

        现在编写一个程序来熟悉一下while语句的书写和使用格式。
        程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

     unsigned char a=0;  //观察这个数最后的变化
     unsigned char b=0;  //观察这个数最后的变化

     unsigned char i;    //控制循环体的条件判断变量

void main() //主函数
{
  i=3;   
  while(i)  //i不断减小,直到变为0时才跳出此循环体
  {
     a=a+1; //当i从3减少到0的时候,这条语句被循环执行了3次。
     i=i-1; //循环的条件不断发生变化,不断减小
  }

  i=0;
  while(i<3)  //i不断增大,当i大于或者等于3时才跳出此循环体
  {
     b=b+2;   //当i从0增加到3的时候,这条语句被循环执行了3次。
     i=i+1;   //循环的条件不断发生变化,不断增加
  }

     View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
     View(b);  //把第2个数b发送到电脑端的串口助手软件上观察。

     while(1)  
     {
     }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


        在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:3
十六进制:3
二进制:11

第2个数
十进制:6
十六进制:6
二进制:110


分析:        
        变量a为3。a初始化为0,进入循环体内后,a每次加1,循环加3次,因此从0变成了3。
        变量b为6。b初始化为0,进入循环体内后,b每次加2,循环加3次,因此从0变成了6。

【48.6   如何在单片机上练习本章节C语言程序?】

        直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
210
luvemcu| | 2016-12-11 23:03 | 只看该作者
ding                                          

使用特权

评论回复
211
jianhong_wu|  楼主 | 2016-12-18 18:50 | 只看该作者
本帖最后由 jianhong_wu 于 2016-12-18 19:20 编辑

第四十九节: 循环语句do while和for。
第四十九节_pdf文件.pdf (76.53 KB)
【49.1   do while语句的常见格式。】

       格式如下:
do
{
         语句1;   
         语句2;
         ……   
         语句N;
} while(条件);


      上述代码,单片机从上往下执行语句,先从do那里无条件进来,从“语句1”开始往下执行,一直执行到“语句N”,才开始判断while(条件)的条件是否为真,如果为真继续返回到do的入口处,继续从“语句1”开始往下执行,依次循环。大家留意到了吗,do while和while语句有什么差别?差别是,do while是先无条件进来执行一次循环体(花括号里所有的程序代码),执行到循环体最底部才判断while(条件)的条件是否为真来决定是否继续循环,先上车再买票。而while语句是先判断条件是否为真再决定是否需要进入循环体,先买票再上车。

【49.2   for语句的简介。】

       for语句也是循环语句,任何for语句能实现的功能都可以用while语句来实现同样的功能,for语句和while语句有什么差别呢?for语句把变量初始化,变量的条件判断,变量在执行循环体后的步进变化这三个常见要素集成在语句内部,以标准的格式书写出来。在很多场合下,for在书写和表达方面比while语句显得更加简洁和直观。

【49.3   for语句的自加格式。】

       格式如下:
for(变量的初始化语句; 变量的条件判断;变量在执行一次循环体后自加的步进变化)
{
      语句1;
      语句2;
      ……
      语句N;
}


        在把上述变成更具体的代码例程如下:
for(i=0; i<3;i++)
{
      语句1;
      语句2;
      ……
      语句N;
}


       上述代码,单片机从上往下,在进入循环体前,先把变量i初始化赋值0(这行初始化代码在整个循环期间只被执行1次),然后判断i是否小于3这个条件,如果此条件为真,就开始正式进入循环体,从“语句1”往下执行到“语句N”,执行完一次循环体后,i就自加1(因为“i++”语句),此时i从原来初始化的0变成了1,接着再返回来到for语句的条件判断”i<3”那里,判断i是否继续满足“小于3”这个条件,如果此条件为真就继续往下执行,否则就跳过循环体结束当前循环。上述for语句实现的功能如果用while语句来写,等效于以下代码:
i=0;  //进入循环体之前先初始化给予初值
while(i<3)
{
     语句1;
     语句2;
     ……
     语句N;
     i++;   //执行一次循环体之后此变量自加发生变化
}


       上述的while循环语句只执行了3次循环体。

【49.4   for语句的自减格式。】

       刚才讲的for(i=0; i<3;i++)这种格式,它的变量i是不断自加的。还有一种比较常见的格式是i不断自减的,它的格式如下:
for(i=3; i>0;i--)
{
     语句1;
     语句2;
     ……
     语句N;
}


       上述自减的for语句功能如果用while语句来写,等效于以下代码:

i=3;  //进入循环体之前先初始化给予初值
while(i>0)
{
     语句1;
     语句2;
     ……
     语句N;
     i--;   //执行一次循环体之后此变量自减发生变化
}


       上述的while循环语句只执行了3次循环体。

【49.5   for省略花括号,没带分号。】

       前面讲的if和while语句中,都提到了省略花括号的情况,for语句也有这种写法,而且省略之后默认的有效范围都是一样的。请看例子如下:
for(i=0; i<3;i++)   //注意,这里没带分号。
         语句1;  
         语句2;
         ……   
         语句N;


        分析:上述代码,跟if语句一样,此时循环体默认只包含“语句1”,等效于:  
for(i=0; i<3;i++)  //注意,这里没带分号。
{
     语句1;  
}
         语句2;
         ……   
         语句N;


【49.6   for省略花括号,带分号。】

for(i=0; i<3;i++);   //注意,这里带分号。
         语句1;  
         语句2;
         ……   
         语句N;


       分析:注意,此时循环体默认不包含“语句1”,而是等效于:   
for(i=0; i<3;i++)
{
    ;  //空语句。
}           
        语句1;
        语句2;
        ……   
        语句N;


        此时循环体内先循环执行三次空语句,然后才会结束for循环,接着才从“语句1”开始往下执行。


【49.7   for循环语句的条件判断。】

      上面举的例子中,仅仅列出了for语句条件判断的小于号关系符“<”,其实,for语句条件判断的关系符跟if语句是一样通用的,凡是if语句能用的关系符都可以用在for语句上,比如“>”,“!=”,“==”,“<=”,“>=”等等。如下:
for(i=0;i<=3;i++);  //小于等于的情况。这种写法是合法的。
for(i=0;i!=3;i++);  //不等于的情况。这种写法是合法的。
for(i=0;i==3;i++);  //等于的情况。这种写法是合法的。


【49.8   例程练习和分析。】

       编写一个程序来熟悉一下do while和for语句的使用。
       程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

    unsigned char a=0;  //观察这个数最后的变化
    unsigned char b=0;  //观察这个数最后的变化
    unsigned char c=0;  //观察这个数最后的变化

unsigned char i;  //控制循环体的条件判断变量

void main() //主函数
{
  i=3;   
  do
  {
     a=a+1;  //每执行一次循环体a就增加1,此行代码被循环执行了3次
     i=i-1;  //i不断变小
  }while(i); //i不断变小,当i变为0时才跳出此循环体

  for(i=0;i<3;i++)
  {
     b=b+2;  //此行代码被循环执行了3次
  }

  for(i=3;i>0;i--)
  {
     c=c+3;  //此行代码被循环执行了3次
  }

      View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
      View(b);  //把第2个数b发送到电脑端的串口助手软件上观察。
      View(c);  //把第3个数c发送到电脑端的串口助手软件上观察。

      while(1)  
      {
      }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


        在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:3
十六进制:3
二进制:11

第2个数
十进制:6
十六进制:6
二进制:110

第3个数
十进制:9
十六进制:9
二进制:1001


分析:        
        变量a为3。a从0开始,循环加1,一共3次,因此等于3。
        变量b为6。b从0开始,循环加2,一共3次,因此等于6。
        变量c为9。c从0开始,循环加3,一共3次,因此等于9。

【49.9   如何在单片机上练习本章节C语言程序?】

        直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
212
jianhong_wu|  楼主 | 2016-12-25 10:41 | 只看该作者
第五十节: 循环体内的continue和break语句。
第五十节_pdf文件.pdf (81.11 KB)
【50.1   continue语句。】

       通常情况下,单片机在循环体里从第一行的“入口条件”开始往下执行,直至碰到循环体的边界“底部花括号”,才又折回到第一行的“入口条件”准备进行新一轮的循环。但是,若中途碰到continue语句,就会提前结束当前这一轮的循环,只要碰到continue语句,就立即折回到第一行的“入口条件”准备进行新一轮的循环。注意,continue语句“结束”的对象仅仅是“当前这一轮的循环”,并没有真正结束这个循环的生命周期。它好像拦路虎,遇到它,它说“你回去,第二天再来。”这台词里的“第二天再来”就强调这个循环体的生命周期还没有真正结束。举一个具体的例子,如下:

while(…)或者for(…)   //循环体的条件判断入口处
{    //循环体开始
    语句1;   
    语句2;
    continue;
    语句3;
    ……   
    语句N;
}    //循环体结束


       分析:上述语句中,单片机从“循环体的条件判断入口处”开始往下执行,碰到continue就马上折回到“循环体的条件判断入口处”,继续开始新一轮的循环,因此,这段代码,continue后面的“语句3”至“语句N”是永远也不会被执行到的。因为continue的拦截,上述语句等效于:

while(…)或者for(…)   //循环体的条件判断入口处
{    //循环体开始
    语句1;   
    语句2;
}    //循环体结束


       问题来了,既然可以如此简化,还要continue干什么,不是多此一举?在实际应用中,continue肯定不会像上面这样单独使用,continue只有跟if语句结合,才有它存在的意义。例如:
while(…)或者for(…)   //循环体的条件判断入口处
{    //循环体开始
    语句1;   
    语句2;
    if(某条件)
    {
         continue;
    }
    语句3;
    ……   
    语句N;
}    //循环体结束


【50.2   break语句。】

        continue语句提前结束当前这一轮的循环,准备进入下一轮的新循环,强调“某次结束”,但不是真结束。break语句是直接跳出当前循环体,是真正的结束当前循环体,强调循环体的“生命结束”。举例如下:

while(…)或者for(…)   //循环体的条件判断入口处
{    //循环体开始
    语句1;   
    语句2;
    break;
    语句3;

    ……   
    语句N;
}    //循环体结束
语句(N+1);  //循环体之外语句


       分析:上述语句中,单片机从“循环体的条件判断入口处”开始往下执行,突然碰到break语句,此时,立即无条件跳出当前循环体(无需判断while或者for的条件),直接执行到循环体之外的“语句(N+1)”,break后面的“语句3”至“语句N”也没有被执行到。实际项目中,break也往往会配合if一起使用,例如:

while(…)或者for(…)   //循环体的条件判断入口处
{    //循环体开始
    语句1;   
    语句2;
    if(某条件)
    {
         break;
    }
    语句3;

    ……   
    语句N;
}    //循环体结束
语句(N+1);  //循环体之外语句


【50.3   break语句能跳多远?】

        break语句能跳多远?预知答案请先看以下例子:
while(…)
{  
      语句1;   
      语句2;
      while(…)
      {   
           语句3;   
           break;
           语句4;
     }   
     语句5;
}   
语句6;


       分析:上述例子中,在while循环里面有藏着第二个while循环,像这种循环之中还有循环的情况,通常称为循环嵌套。单片机从上往下执行,当遇到break后,它会跳到“语句5”那里呢,还是会跳到“语句6”那里?正确答案是“语句5”那里,这说明了break语句的“有效射程”仅仅刚好能跳出当前的循环体。也就是说,在上述循环嵌套的例子中,最内层的break只能跳出最内层的循环体,不能跳到最外层的“语句6”那里,如果需要继续跳出最外层的“语句6”那里,可以继续在外层的循环体内再增加一个break语句。

【50.4   还有哪些语句可以无条件跳出循环体?】

      除了break以外,还有return和goto语句可以跳出循环体。这部分的内容大家只需大概了解一下即可。return语句比break语句还厉害,它不仅仅跳出当前循环体,还是跳出了当前函数,也就是提前结束了当前函数,这部分的内容后面章节会讲到,暂时不用管。而goto语句在C语言中大家都公认不建议用,因为它很容易扰乱大家常用的C语言编程结构,我本人也从来没有用过goto语句,因此不再深入讲解它。

【50.5   例程练习和分析。】

      编写一个程序来熟悉一下continue和break语句的使用。
      程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

  unsigned char a=0;  //观察这个数最后的变化
  unsigned char b=0;  //观察这个数最后的变化
  unsigned char c=0;  //观察这个数最后的变化
  unsigned char d=0;  //观察这个数最后的变化

  unsigned char i;  //控制循环体的条件判断变量
void main() //主函数
{
  //i<6的条件判断是在进入循环体之前判断,而i的自加1是在执行完一次循环体之后才自加的。
  for(i=0;i<6;i++)  
  {
     a=a+1;    //被执行了6次,分别是第0,1,2,3,4,5次
     if(i>=3)  //当i等于3的时候,开始“拦截”continue后面的代码。
     {
        continue;  //提前结束本次循环,准备进入下一次循环
     }
     b=b+1;  //被执行了3次,分别是第0,1,2次
  }

  //i<6的条件判断是在进入循环体之前判断,而i的自加1是在执行完一次循环体之后才自加的。
  for(i=0;i<6;i++)  
  {
     c=c+1;   //被执行了4次,分别是第0,1,2,3次
     if(i>=3)  //当i等于3的时候,直接跳出当前循环体,结束此循环体的“生命周期”。
     {
        break; //马上跳出当前循环体
     }
     d=d+1;   //被执行了3次,分别是第0,1,2次
  }

      View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
      View(b);  //把第2个数b发送到电脑端的串口助手软件上观察。
      View(c);  //把第3个数c发送到电脑端的串口助手软件上观察。
      View(d);  //把第4个数d发送到电脑端的串口助手软件上观察。

      while(1)  
      {
      }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


       在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:6
十六进制:6
二进制:110

第2个数
十进制:3
十六进制:3
二进制:11

第3个数
十进制:4
十六进制:4
二进制:100

第4个数
十进制:3
十六进制:3
二进制:11


分析:        
       变量a为6。
       变量b为3。
       变量c为4。     
       变量d为3。

【50.6   如何在单片机上练习本章节C语言程序?】

       直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
213
jianhong_wu|  楼主 | 2017-1-4 10:31 | 只看该作者
第五十一节: for和while的循环嵌套。
第五十一节_pdf文件.pdf (75.16 KB)
【51.1   循环的嵌套。】

       大循环的内部又包含了小循环,称为循环嵌套。生活中,循环嵌套的现象很常见,一年12个月,假设每个月都是30天(仅仅假设而已),1月份30天,2月份30天......11月份30天,12月份30,这里的年就是大循环,年内部的月就是小循环。一年12个月,大循环就是12次。一个月30天,小循环就是30次。用for语句来表达,大意如下:
for(m=1;m<=12;m++)  //大循环。一年12个月。这里的m看作月,代表一年12个月的大循环。
{
    for(d=1;d<=30;d++) //内嵌小循环。一月30天。这里的d看作天,代表一个月30天的小循环。
        {

    }
    }


【51.2   循环嵌套的执行顺序。】

        例子如下:
for(i=0;i<2;i++)  //大循环
{
        语句1;
        for(k=0;k<3;k++) //内嵌的小循环
        {
            语句2;
        }
        语句3;
}

       上述例子中,带i的for称为大循环,带k的for称为小循环,单片机从大循环入口进来,由上往下执行,执行第1次大循环,先执行1次“语句1”,接着进入小循环,小循环要连续循环执行3次“语句2”才跳出小循环,之后执行1次“语句3”,然后再返回到大循环入口判断i条件是否满足,此时条件满足,继续执行第2次大循环,1次“语句1”,3次“语句2”,1次“语句3”,第2次循环结束后又返回到大循环入口判断i条件,此时i已经等于2不再小于2了,因此条件不满足,结束整个循环嵌套。上述执行的语句顺序如下:
       语句1;   //第1次大循环开始
       语句2;   
       语句2;
       语句2;
       语句3;
       语句1;   //第2次大循环开始
       语句2;
       语句2;
       语句2;
       语句3;

       根据此顺序,再看一个具体的程序例子:
a=0;  
b=0;
for(i=0;i<2;i++)  //大循环
{
     a=a+1;  //被执行了2次
     for(k=0;k<3;k++) //内嵌的小循环
     {
           b= b+1;  //被执行了6次
     }
}

      上述例子中,执行完程序后,a的值变成了2,b的值变成了6。重点分析b的变化,“b=b+1”在内嵌循环体里被执行了6次,6次从何而来?就是i乘以k等于6。这个乘法次数是循环嵌套一个很重要的特性。上述程序如果用while语句来实现,等效如下:
a=0;  
b=0;
i=0;  //控制大循环的变量初始化
while(i<2)  //大循环
{
     a=a+1;  //被执行了2次
     k=0;    //控制小循环的变量初始化
     while(k<3) //内嵌的小循环
     {
         b= b+1;  //被执行了6次
         k=k+1;
    }
     i=i+1;
}


【51.3   循环嵌套的常见用途---二维数组的应用。】

       二维数组a[2][3],它有6个变量,在没有学for语句之前,如果要依次把每个元素单独赋值清零真不容易,要写6次赋值语句如下:
a[0][0]=0;
a[0][1]=0;
a[0][2]=0;
a[1][0]=0;
a[1][1]=0;
a[1][2]=0;


       自从懂了for嵌套语句之后,可以让同样功能的代码简洁许多。上述代码等效于如下:
for(i=0;i<2;i++)  //大循环
{
     for(k=0;k<3;k++) //内嵌的小循环
     {
          a[i][k]=0;
      }
}


【51.4   循环嵌套的常见用途---大延时。】

        单片机项目会经常会用到delay这个延时函数,大部分都是利用for循环来实现,小延时的函数往往不用嵌套,直接如下编写:
    for(k=0;k<N;k++);  

       上述的N是控制循环次数,每次循环都要消耗单片机一点时间,如果N越大需要消耗的时间就越多,起到延时的作用。但是N所能取的最大值受它所定义的类型所限制,比如unsigned char类型最大范围是255,unsigned int类型最大范围是65535,unsigned long类型最大范围是4294967295。如果要实现更大的延时怎么办?就可以用for的循环嵌套,利用循环嵌套可以使得循环总次数进行乘法翻倍的放大,很容易编写大延时的函数。比如:
for(i=0;i<M;i++)  //大循环
{
      for(k=0;k<N;k++); //内嵌的小循环
}

       此时循环的次数是N乘以M的乘积。如果N和M都是unsigned long类型,就意味着最大循环次数是4294967295的平方,次数大到惊人。

【51.5   例程练习和分析。】

       现在编写一个循环嵌套的练习程序。
       程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

      unsigned char a=0;  //观察这个数最后的变化
      unsigned char b=0;  //观察这个数最后的变化
      unsigned char c=0;  //观察这个数最后的变化

      unsigned char i;  //控制大循环体的条件判断变量
      unsigned char k;  //控制内嵌小循环体的条件判断变量
void main() //主函数
{
  for(i=0;i<2;i++)  //大循环
  {
     a=a+1;    //被执行了2次
     for(k=0;k<3;k++)  //内嵌小循环
     {  
          b=b+1;  //被执行了6次,也就是i乘以k,2乘以3等于6.
     }
     c=c+1;    //被执行了2次
  }

      View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
      View(b);  //把第2个数b发送到电脑端的串口助手软件上观察。
      View(c);  //把第3个数c发送到电脑端的串口助手软件上观察。

      while(1)  
      {
      }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


       在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:2
十六进制:2
二进制:10

第2个数
十进制:6
十六进制:6
二进制:110

第3个数
十进制:2
十六进制:2
二进制:10


分析:        
    变量a为2。
    变量b为6。
    变量c为2。

【51.6   如何在单片机上练习本章节C语言程序?】

       直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
214
srxz| | 2017-1-6 16:25 | 只看该作者
楼主辛苦

使用特权

评论回复
215
jianhong_wu|  楼主 | 2017-1-8 11:30 | 只看该作者
第五十二节: 支撑程序框架的switch语句。
第五十二节_pdf文件.pdf (83.37 KB)
【52.1   switch的重要性。】

       switch是非常重要的语句,我所有的单片机项目都是用switch搭建程序主框架。如果说while和for是一对孪生兄弟,那么“if-else if”和switch也是一对孪生兄弟,凡是用“if-else if”能实现的功能都可以用switch实现。switch有条件分支的功能,当条件的分支超过3个以上时,switch会比“if-else if”更加直观清晰。

【52.2   switch的语法。】

        switch常见的格式如下:
switch(变量)    //根据变量的数值大小从对应的case入口进来
{
     case 0:   //入口0
         语句0;
         break; //switch程序体的出口之一
     case 1:  //入口1
         语句1;
         break; //switch程序体的出口之一
     case 2: //入口2
         语句2;
         break; //switch程序体的出口之一
}    //最下面的花括号也是一个switch程序体的出口之一


       分析:单片机从第一行的switch(变量)进来,依次往下查询跟变量匹配的case入口,然后从匹配的case入口进来,往下执行语句,直到遇上break语句,或者return语句,或者“最下面的花括号”这三种情况之一,才跳出当前switch程序体。上述例子中,假如变量等于3,单片机从switch(变量)进来,往下查询跟3匹配的case入口,因为没有发现case 3,最后遇到“最下面的花括号”于是结束switch程序体,像这种变量等于3的情况,就意味着switch里面的有效语句没有被执行到。多补充一句,在case 2选项中,“语句2”后面紧跟的break可以省略,因为case 2是最后一个case,即使没有遇到break也会遇到“最下面的花括号”而结束switch程序体。上述程序功能如果用“if-else if”语句来实现,等效于如下:
if(0==变量)
{
    语句0;
}
else if(1==变量)
{
    语句1;
}
else if(2==变量)
{
    语句2;
}


【52.3   switch的break。】

       刚才的例子中,可以看到三个关键字:switch,case,break。其实并不是每个case都必须要跟break配套,break只是起到一个出口的功能。假如没有遇到break,程序会一直往下执行,直到遇到break或者switch“最下面的花括号”为止。比如:
switch(变量)    //根据变量的数值大小从对应的case入口进来
{
     case 0:   //入口0
         语句0;
         break;
     case 1:  //入口1
         语句1;
     case 2: //入口2
         语句2;
         break;
     case 3: //入口3
         语句3;
         break;
}   //最下面的花括号也是一个switch程序体的出口之一


       分析:假如此时switch(变量)的变量等于1,单片机经过查询后,就从匹配的case 1入口进来,执行“语句1”后,居然没有遇到break语句,于是紧接着碰到“case 2”入口的语句,现在问题来了,单片机此时是退出switch程序体还是忽略“case 2”入口语句而继续执行后面的“语句2”?答案是:忽略“case 2”入口语句而继续执行后面的“语句2”。这里有点像坐地铁,你只关注一个入口和一个出口,进入地铁内之后,你中途再遇到无数个入口都可以忽略而继续前进,直到你到达目的地的出口才结束整个乘车过程。继续刚才的分析,单片机执行“语句2”之后,紧接着遇到break语句,这时才跳出整个switch程序体。回顾一下整个流程,本例子中case 1没有break语句,就继续往下执行下面case2里面的语句,直到遇到break或者“最下面的花括号”为止。

【52.4   case的变量有顺序要求吗?】

       switch语句内部的case有规定顺序吗?必须连贯吗?switch程序体内部可以写很多case入口,这些case入口是不是必须按从小到大的顺序?是不是规定必须case数字连贯?答案是:没有规定顺序,也没有规定case数字连贯。case的数值只是代表入口,比如以下两种写法都是合法的:
       第一种:case不按从小到大的顺序(这种格式是合法的):
switch(变量)   
{
     case 2:
         语句2;
         break;
     case 0:  
         语句0;
         break;
     case 1:  
         语句1;
         break;
}   


       第二种:case的数字不连贯(这种格式也是合法的):
switch(变量)   
{
    case 0:  
         语句0;
         break;
     case 3:
         语句3;
         break;
     case 9:  
         语句9;
         break;
}   


【52.5   switch的default。】

       default是入口语句,它在switch语句中也不是必须的,应根据程序需要来选择。default相当于“if-else if-else ”组合语句中的else,也就是当switch的入口变量没有匹配的case入口时,就会默认进入default入口,就像“if-else if-else ”语句中当前面所有的条件不满足时,就进入else语句的程序体,比如:
switch(变量)    //根据变量的数值大小从对应的case入口进来
{
     case 0:   //入口0
         语句0;
         break; //switch程序体的出口之一
     case 1:  //入口1
         语句1;
         break; //switch程序体的出口之一
     case 2: //入口2
         语句2;
         break; //switch程序体的出口之一
     default:  //当所有的case不满足,就从default的入口进来
         语句3;
         break;
}    //最下面的花括号也是一个switch程序体的出口之一


       分析:假如switch的入口变量等于35,单片机从上往下查询,因为没有找到case 35,所以就会从默认的default入口进来执行” 语句3”,然后遇到break语句才跳出switch程序体。上述程序功能如果用“if-else if-else”组合语句来实现等效于如下:
if(0==变量)
{
    语句0;
}
else if(1==变量)
{
    语句1;
}
else if(2==变量)
{
    语句2;
}
else   //相当于switch中的default
{
    语句3;
}


【52.6   switch中内嵌switch。】

       if语句可以内嵌if语句,while语句也可以内嵌while语句,switch语句当然也可以内嵌switch。比如:
switch(a)
{
   case 1:
        switch(b)  //内嵌的switch
        {
             case 1:
                  Break;
             case 2:
                  Break;
        }
        Break;
   case 2:
        Break;
}


        分析:上述这种switch内嵌switch语句也是合法的,而且在实际项目中也很常用,大家目前先有个大概的了解即可,暂时不深入讲解。

【52.7   例程练习和分析。】

        现在编写一个switch的练习程序。
        程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/

     unsigned char k;   //switch的入口变量
     unsigned char a;   //观察此变量的变化来理解switch的执行顺序
void main() //主函数
{
      a=0;
      k=2;   //入口变量等于2
  switch(k)
  {
     case 0:  //入口0
       a++;
       break; //跳出switch
     case 1:  //入口1
       a++;
     case 2:  //入口2,上述k等于2所以从这里进来
       a++;
     case 3:  //入口3
       a++;
     case 4:  //入口4
       a++;
       break;  //跳出switch
     case 5:  //入口5
       a++;
       break;  //跳出switch
     default:  //当前面没有遇到匹配的case入口时,就从此default入口进来
       a++;
       break;  //跳出switch
  }            //最后一个switch的花括号也是跳出switch

      View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。

      while(1)  
      {
      }
}

/*---C语言学习区域的结束。-----------------------------------------------*/


       在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:3
十六进制:3
二进制:11


分析:        
        变量a为3。单片机从case 2入口进来,因为case 2和case 3都没有break语句,直到遇到case 4的break语句才结束switch程序体,因此整个过程遇到了3次“a++”语句,因此变量a的“自加一”执行了3次后从0变成了3。

【52.8   如何在单片机上练习本章节C语言程序?】

       直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
评论
zhangtao3b608 2021-5-15 16:21 回复TA
感谢 
216
srxz| | 2017-1-11 14:55 | 只看该作者
  更新了   

使用特权

评论回复
217
jianhong_wu|  楼主 | 2017-1-15 14:25 | 只看该作者
本帖最后由 jianhong_wu 于 2017-1-15 14:44 编辑

第五十三节: 使用函数的三要素和执行顺序。
第五十三节_pdf文件.pdf (76.21 KB)
【53.1   函数的十大关联部件。】

       函数是什么?我很难用一句话给它下定义,哪怕我真能用一句话定义了,初学者也很难从一句话的定义中“格”出函数之理。之所以函数有如此玄机,确实因为它包罗万象,涉及的内容非常多,就像要我去定义什么是中国,我也没法用一句话去定义,只有长大了慢慢了解它的地理文化历史,你才会对咱中国有深刻的认识。函数也是如此,虽然我不能用一句话定义函数,但是函数跟十大部件有关,只要今后不断学习和运用,对十大部件各个击破直到全部“通关”,总有一天你会感悟到函数的精髓。现在先把十大部件列出来,让大家有一个感性的认识,它们是:函数体,函数接口,return语句,堆栈,全局变量,普通局部变量,静态局部变量,单个变量的指针,数组的指针,结构体的指针。本节讲的“使用函数的三要素和执行顺序”就是属于“函数体”这个部件的内容。

【53.2   使用函数的三要素。】

       有的人习惯把函数称为程序,比如主程序,子程序,这时的主程序对应主函数,子程序对应子函数,是一回事,只是每个人的表达习惯不一样而已。使用函数的三要素是声明,定义,调用。每次新构造一个函数时,尽量遵守这个三个要素来做就可以减少一些差错。什么叫函数的声明,定义,调用?为了让大家有一个感性的认识,请先看下面这个例子:
/*---C语言学习区域的开始。-----------------------------------------------*/

void HanShu(void);   //子函数声明的第一区域

unsigned char  a;   //全局变量定义的第二区域
unsigned char  b;
unsigned char  c;

void HanShu(void)    //子函数定义的第三区域
{
   a++;    //子函数的代码语句
   b=b+5;
   c=c+6;
}

void main() //主函数
{
   a=0;
   b=0;
   c=0;
   HanShu() ;      //子函数被调用的第四区域
   c=a+b;
   while(1)  
   {

   }
}
/*---C语言学习区域的结束。-----------------------------------------------*/


        分析:上述例子中,从书写代码区域的角度来寻找函数的大概规律,从上往下:
        第一区域:写子函数HanShu声明。
        第二区域:全局变量的定义。
        第三区域:子函数HanShu的定义。
        第四区域:在main函数里对子函数HanShu的调用。

【53.3   子函数被其它函数调用时候的执行顺序。】

       子函数被其它函数调用时,子函数的名字就相当于一个跳转地址,而子函数的定义部分就是要跳转的实际地址,单片机在主函数里遇到子函数名字,就直接跳转到子函数定义那里执行子函数内部的代码,执行完子函数后再返回到主函数,此时返回到主函数哪里呢?答:因为子函数已经被执行了一次,所以返回到主函数中的子函数名字后面,然后继续往下执行main函数其它剩余的代码。请看下面这个代码的执行顺序,一目了然:
/*---C语言学习区域的开始。-----------------------------------------------*/

   void HanShu(void);   //子函数的声明
   void HanShu(void)    //子函数的定义
   {
      语句1;
      语句2;
   }
   void main() //主函数
   {
      语句3;
      HanShu() ;      //子函数的被调用
      语句4;

      while(1)  
      {

      }
}
/*---C语言学习区域的结束。-----------------------------------------------*/


        执行顺序分析:单片机从主函数main那里进来往下执行,先执行“语句3”,接着遇到HanShu名字的跳转地址,然后马上跳转到HanShu的定义部分,执行“语句1”,“语句2”,执行完子函数HanShu的定义部分,就马上返回到主函数,继续执行HanShu名字后面的“语句4”。整个执行语句的先后顺序如下:
        语句3;
        语句1;
        语句2;
        语句4;


【53.4   例程练习和分析。】

        现在编写一个练习程序来体验一下函数的使用。
        程序代码如下:

/*---C语言学习区域的开始。-----------------------------------------------*/
void HanShu(void);   //子函数声明的第一区域

unsigned char  a;   //全局变量定义的第二区域

void HanShu(void)    //子函数定义的第三区域
{
   a++;    //子函数的代码语句
}
void main() //主函数
{
   a=0;
   a++;
   HanShu() ;      //子函数被调用的第四区域
   a++;
   View(a);  //把第1个数a发送到电脑端的串口助手软件上观察。
   while(1)  
   {

   }
}
/*---C语言学习区域的结束。-----------------------------------------------*/


        在电脑串口助手软件上观察到的程序执行现象如下:
开始...

第1个数
十进制:3
十六进制:3
二进制:11


分析:        
        变量a为3。单片机从main主函数进来,主函数里有2条“a++”,再加上子函数里也有1条“a++”,因此累加了3次,从0变成了3.

【53.5   如何在单片机上练习本章节C语言程序?】

        直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。

使用特权

评论回复
评论
zhangtao3b608 2021-5-15 16:24 回复TA
顶一下 
218
1912740166| | 2017-1-17 17:50 | 只看该作者
顶一下,到此一游

使用特权

评论回复
219
power605112| | 2017-1-18 11:26 | 只看该作者
这个要点个赞,虽然我只是爱好者

使用特权

评论回复
220
jiaowozange| | 2017-1-19 17:27 | 只看该作者
jianhong_wu 发表于 2016-1-18 11:14
第一节:我的价值观。      我2006年毕业,2009年就出来做自由职业者在深圳以接单片机项目谋生,到现在我有 ...

顶一下,持续关注

使用特权

评论回复
发新帖 本帖赏金 42.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则