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

[复制链接]
363050|721
 楼主| jianhong_wu 发表于 2016-6-27 11:11 | 显示全部楼层
第二十四节:借用unsigned long类型的中间变量可以减少溢出现象。

【24.1   为什么要借用unsigned long类型的中间变量?】

       为什么要借用unsigned long类型的中间变量进行算术运算?其实就是为了减少溢出的问题。溢出是因为数据超过了它的最大范围,unsigned char ,unsigned int ,unsigned long三种数据类型中,unsigned long的取值是最大的。当参与运算变量中存在非unsigned long类型的时候,在运算前,先让每个非unsigned long类型的变量借用一个unsigned long类型的中间变量,然后才开始运算,可以大大减少运算中的溢出问题。 unsigned long的取值是从0到4294967295,万一数据超过了4294967295怎么办?可用BCD码的数组方式进行运算,这种数组运算的方法我以后会跟大家介绍,初学者现在暂时不用深入了解它。

【24.2   如何借用unsigned long类型的中间变量?】

        借用中间变量的方法是引入中间变量,有多少个非unsigned long类型变量就引入多少个unsigned long中间变量,再借这个“壳”进行运算,最后再把中间变量的计算结果返回给实际变量。请看下面例子。

        转换之前:
  1. unsigned int  a;
  2. unsigned char x=195;
  3. unsigned long y=101;
  4. a=x-y;      //进行算术减法运算

       分析:
      上述公式用到3个变量,其中a和x都不是unsigned long变量,因此需要为它们分别引入两个unsigned long类型的中间变量t和s,于是乎,继续往下看......

       转换之后:
  1. unsigned int  a;
  2. unsigned char x=195;
  3. unsigned long y=101;

  4. unsigned long t; //引入的中间变量t,用来给a借用。
  5. unsigned long s; //引入的中间变量s,用来给x借用。

  6. //第一步:使用之前先清零
  7. t=0;             //t在用之前,先把t的32位全部清零。
  8. s=0;             //s在用之前,先把s的32位全部清零。

  9. s=x;             //s接收x原数据,等效于x借用unsigned long中间变量s这个壳。
  10. t=s-y;           //此处unsigned long类型的t就默认代表了unsigned int类型的变量a。

  11. //第二步:因为其它的变量都是临时的,所以运算结束后再返回计算结果给原来的变量。
  12. a=t;             //运算结束后再把计算结果返回给原来的变量a。

      分析:
      第一步:unsigned long类型的中间变量在转换之前为什么要先赋值0进行清零,比如上述代码的“s=0;”?因为它是32位的数据类型,它也是一个随机数,如果不清零,后续的其它类型的变量可能是16位或者8位的类型变量,这些宽度不一的变量在给32位的变量赋值的时候,只能覆盖到32位变量的低16位或者低8位,无法等效于实际借用者变量的数值,所以有可能会出错。
      第二步:因为其它的变量都是临时的,所以运算结束后应该再返回计算结果给原来的实际变量。在这里要多说一句,实际项目中,最后接收运算结果的变量应该根据项目所需去选择它的类型,建议尽量选择unsigned long类型吧,否则,如果中间变量的计算结果大于接收变量本身的类型范围,也会发生溢出。比如,上述最后一行代码a=t,如果此时t的数值大于65535,a也会发生溢出的现象。 但是如果a本身是unsigned long 类型,就不会发生这种现象。
       加法,乘法,除法在借用中间变量的时候,跟本节减法例子中的思路也大同小异。

【24.3   建议在算术运算中确保所有的变量都是unsigned long类型。】

       不管是以前讲的加法,现在讲的减法,还是未来讲的乘法和除法,我都会建议“在加减乘除四则运算中,凡是非unsigned long类型的变量,都应该借用unsigned long类型的中间变量进行运算,最后再返回计算结果给实际的变量。”unsigned long变量是三种数据类型中取值范围最大的数,借用此类型的中间变量,可以减少在简单运算中可能出现的溢出问题。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
kiki@cjy 发表于 2016-6-30 10:34 | 显示全部楼层
给楼主10个赞.这么独具匠心的好资料不出书太可惜了
 楼主| jianhong_wu 发表于 2016-7-4 09:14 | 显示全部楼层
第二十五节:乘法运算中的5种常用组合。

【25.1   乘法语法格式。】

      乘法语法格式:
      “保存变量”=“乘数1”*“乘数2”*..*“乘数N”;
      含义:为什么C语言的乘法符号并不是我们熟悉的“X”而是“*”?我猜测是因为“X”跟键盘的大写字母“X”重复有冲突了,而“*”轮廓跟“X”很相似,并且也可以在键盘上通过“Shift+8”的组合键直接键入“*”,所以用“*”作为乘法符号。上述乘法格式中,右边的“乘数”与“乘数”相乘(这里暂时把平时所说的被乘数也归类为乘数),并且把最终的运算结果赋值给左边的“保存变量”。注意,这里的符号“=”不是等于号的意思,而是赋值的意思。左边的“保存变量”必须是变量,不能是常量,否则编译时会报错。右边的“乘数”既可以是变量,也可以是常量,也可以是“保存变量”本身自己。多说一句,什么是变量和常量?变量是可以在程序中被更改的,被分配的一个RAM空间。常量往往是数字,或者被分配在ROM空间的一个具体数值。下面根据右边“乘数”与“乘数”的不同组合,列出了乘法运算的5种常用组合。

       第1种:“乘数1”是常量,“乘数2”是常量。比如:
  1.        unsigned char a;
  2.        a=15*3;

       分析:数字“15”和“3”都是常量。执行上述语句后,保存变量a变成了45。

       第2种:“乘数1”是变量,“乘数2”是常量。比如:
  1.        unsigned char b;
  2.        unsigned char x=15;
  3.        b=x*10;

       分析:x是变量,“10”是常量。由于原来x变量里面的数值是15,执行上述语句后,保存变量b变成了150。而变量x则保持不变,x还是15。

       第3种:“乘数1”是变量,“乘数2”是变量。比如:
  1.        unsigned char c;
  2.        unsigned char x=15;
  3.        unsigned char y=6;
  4.        c=x*y;

       分析:x是变量,y也是变量。由于原来x变量里面的数值是15,y变量里面的数值是6,执行上述语句后,保存变量c变成了90。而变量x和y则保持不变,x还是15,y还是6。

       第4种:“乘数1”是保存变量本身,“乘数2”是常量。比如:
  1.        unsigned char d=18;
  2.        d=d*2;
  3.        d=d*7;

       分析:d是保存变量,“2”和“7”都是常量。这类语句有一个特点,具备了自乘功能,可以更改自己本身的数值。 比如原来保存变量d的数值是18,执行“d=d*2;”语句后,d变成了36,接着再执行完“d=d*7;”语句后,d最后变成了252。

      第5种:“乘数1”是保存变量本身,“乘数2”是变量。比如:
  1.       unsigned char e=2;
  2.       unsigned char x=15;
  3.       unsigned char y=6;
  4.       e=e*x;
  5.       e=e*y;

      分析:e是保存变量,x与y都是变量。这类语句有一个特点,具备了自乘功能,可以更改自己本身的数值。比如原来保存变量e的数值是2,执行“e=e*x;”语句后,e变成了30,接着再执行完“e=e*y;”语句后,e最后变成了180。

【25.2   例程练习和分析。】

      现在我们编写一个程序来验证上面讲到的5个乘法例子:
      程序代码如下:

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

  2. void main() //主函数
  3. {
  4.     unsigned char a;     //定义一个变量a,并且分配了1个字节的RAM空间。
  5.     unsigned char b;     //定义一个变量b,并且分配了1个字节的RAM空间。
  6.     unsigned char c;     //定义一个变量c,并且分配了1个字节的RAM空间。
  7.     unsigned char d=18;  //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为18.
  8.     unsigned char e=2;   //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为2.

  9.     unsigned char x=15;  //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为15.
  10.     unsigned char y=6;   //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        

  11.     //第1种:“乘数1”是常量,“乘数2”是常量。
  12.     a=15*3;

  13.     //第2种:“乘数1”是变量,“乘数2”是常量。
  14.     b=x*10;

  15.     //第3种:“乘数1”是变量,“乘数2”是变量。
  16.     c=x*y;

  17.     //第4种:“乘数1”是保存变量本身,“乘数2”是常量。
  18.     d=d*2;
  19.     d=d*7;

  20.     //第5种:“乘数1”是保存变量本身,“乘数2”是变量。
  21.     e=e*x;
  22.     e=e*y;

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

  28.      while(1)  
  29.      {
  30.      }
  31. }

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


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

  1. 开始...

  2. 第1个数
  3. 十进制:45
  4. 十六进制:2D
  5. 二进制:101101

  6. 第2个数
  7. 十进制:150
  8. 十六进制:96
  9. 二进制:10010110

  10. 第3个数
  11. 十进制:90
  12. 十六进制:5A
  13. 二进制:1011010

  14. 第4个数
  15. 十进制:252
  16. 十六进制:FC
  17. 二进制:11111100

  18. 第5个数
  19. 十进制:180
  20. 十六进制:B4
  21. 二进制:10110100

分析:        
         通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
乐逍遥6 发表于 2016-7-7 14:21 | 显示全部楼层
追帖中
 楼主| jianhong_wu 发表于 2016-7-12 10:14 | 显示全部楼层
第二十六节:连乘、自乘、自乘简写,溢出。

【26.1   连乘。】

      上一节的乘法例子中,右边的乘数只有两个。实际上,C语言规则没有限制乘数的个数,它的通用格式如下:
  1.        “保存变量”=“乘数1”*“乘数2”...*“乘数N”;

      当右边的乘数个数超过两个的时候(这里暂时把平时所说的被乘数也归类为乘数),这种情况就是“连乘”。每个乘数的属性没有限定,可以是常量,也可以是变量。比如:
  1.       unsigned char x=3;   //定义一个变量x,初始化默认为3.
  2.       unsigned char y=6;   //定义一个变量y,初始化默认为6.      
  3.       unsigned char k=2;   //定义一个变量k,初始化默认为2.
  4.       a=2*5*3;  //乘数全部是常量。a的结果为30。
  5.       b=k*x*y;  //乘全部是变量。b的结果为36。
  6.       c=x*5*y;  //乘数,有的是常量,有的是变量。c的结果为90。

      连乘的运行顺序是,赋值符号“=”右边的乘数挨个相乘,把每一次的运算结果放在一个临时的隐蔽中间变量里,这个隐蔽的变量我们看不到,是单片机系统内部参与运算时的专用寄存器,等右边所有乘数连乘的计算结果出来后,再把隐蔽变量所保存的计算结果赋值给左边的“保存变量”。

【26.2   自乘与自乘简写。】

      什么是自乘?当赋值符号“=”右边的乘数只要其中有一个是“保存变量”本身时,这种情况就是“自乘”,常见格式如下:
  1.       “保存变量”=“保存变量”*“乘数1”;
  2.       “保存变量”=“保存变量”*(“乘数1”*“乘数2”...*“乘数N”);

      上述自乘计算式可以简写成如下格式:
  1.        “保存变量”*=“乘数1”;
  2.        “保存变量”*=“乘数1”*“乘数2”...*“乘数N”;

       这种格式就是“自乘简写”。现在举几个例子如下:
  1.        unsigned char d=5;       //定义一个变量d,初始化默认为5.
  2.        unsigned char e=5;       //定义一个变量e,初始化默认为5.
  3.        unsigned char f=5;       //定义一个变量f,初始化默认为5.

  4.        unsigned char x=3;   //定义一个变量x,初始化默认为3.
  5.        unsigned char y=6;   //定义一个变量y,初始化默认为6.      
  6.        unsigned char k=2;   //定义一个变量k,初始化默认为2.
  7.        d*=6;     //相当于d=d*6;最后d的结果为30。
  8.        e*=x;     //相当于e=e*x;最后e的结果为15。
  9.        f*=2*y*k; //相当于f=f*(2*y*k);最后f的结果为120。


【26.3   有没有“自乘1”的特殊写法?】

      之前在讲加法的自加和减法的自减运算时,还给大家介绍了它们另外一种特殊的简写方式。比如减法运算,当右边只有2减数,当一个减数是“保存变量”,另一个是常数1时,格式如下:
  1.        “保存变量”=“保存变量”-1;

       这时候,可以把上述格式简写成如下两种格式:
  1.        “保存变量”--;
  2.       --“保存变量”;

      这两种格式也是俗称的“自减1”操作。比如:
  1.        g--;  //相当于g=g-1或者g-=1;
  2.        --h;  //相当于h=h-1或者h-=1;

      那么,本节所讲的自乘运算,有没有“g**”或者“**h”这种特殊的“自乘1”写法?答案很明显,C语言里没有“自乘1”这种特殊写法。因为任何一个数“自乘1”还是等于它本身,所以在乘法运算中这种特殊写法就没有存在的意义。多说一句,如果某天有朋友在某个地方看到“**h”这类语句,它的本意跟“自乘”没关系,而是跟C语言的另一块知识点“指针”有关。

【26.4   乘法的溢出。】

      乘法的溢出规律跟加减法的溢出规律是一样的。举一个例子如下:
  1.       unsigned char m=30;
  2.       unsigned char n=10;
  3.       unsigned char a;
  4.       a=m*n;  

      分析:m与n相乘,相当于30乘以10,运算结果是300(十六进制是0x012c)保存在一个隐藏中间变量,根据前面加减法运算的规律,我猜测这个隐藏中间变量可能是unsigned int类型,然后再把这个中间变量赋值给单字节变量a,a只能接收十六进制的低8位字节0x2c,所以运算后a的数值由于溢出变成了十六进制的0x2c(十进制是44)。由于乘法的溢出规律跟加减法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了减少溢出的现象,我建议,不管加减乘除,凡是参与运算的变量全部都应该转化成unsigned long变量,转化的方法已经在前面章节讲过,不再重复讲解这方面的内容。

【26.5   例程练习和分析。】

       现在编写一个程序来验证刚才讲到的连乘和自乘简写:
       程序代码如下:

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

  2. void main() //主函数
  3. {
  4.     unsigned char a;      
  5.     unsigned char b;      
  6. unsigned char c;      
  7.     unsigned char d=5;      //定义一个变量d,初始化默认为5.
  8.     unsigned char e=5;      //定义一个变量e,初始化默认为5.
  9.     unsigned char f=5;      //定义一个变量f,初始化默认为5.

  10.     unsigned char x=3;      //定义一个变量x,初始化默认为3.
  11.     unsigned char y=6;      //定义一个变量y,初始化默认为6.        
  12.      unsigned char k=2;     //定义一个变量k,初始化默认为2.

  13.      //第1个知识点:连乘。
  14.      a=2*5*3;              //乘数全部是常量。a的结果为30。
  15.      b=k*x*y;              //乘数全部是变量。b的结果为36。
  16.      c=x*5*y;              //乘数,有的是常量,有的是变量。c的结果为90。

  17.      //第2个知识点:自乘的简写。
  18.      d*=6;                 //相当于d=d*6;最后d的结果为30。
  19.      e*=x;                 //相当于e=e*x;最后e的结果为15。
  20.      f*=2*y*k;             //相当于f=f*(2*y*k);最后f的结果为120。

  21.      View(a);              //把第1个数a发送到电脑端的串口助手软件上观察。
  22.      View(b);              //把第2个数b发送到电脑端的串口助手软件上观察。
  23.      View(c);              //把第3个数c发送到电脑端的串口助手软件上观察。
  24.      View(d);              //把第4个数d发送到电脑端的串口助手软件上观察。
  25.      View(e);              //把第5个数e发送到电脑端的串口助手软件上观察。
  26.      View(f);              //把第6个数f发送到电脑端的串口助手软件上观察。

  27.      while(1)  
  28.      {
  29.      }
  30. }

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


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

  1. 开始...

  2. 第1个数
  3. 十进制:30
  4. 十六进制:1E
  5. 二进制:11110

  6. 第2个数
  7. 十进制:36
  8. 十六进制:24
  9. 二进制:100100

  10. 第3个数
  11. 十进制:90
  12. 十六进制:5A
  13. 二进制:1011010

  14. 第4个数
  15. 十进制:30
  16. 十六进制:1E
  17. 二进制:11110

  18. 第5个数
  19. 十进制:15
  20. 十六进制:F
  21. 二进制:1111

  22. 第6个数
  23. 十进制:120
  24. 十六进制:78
  25. 二进制:1111000


分析:        
       通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| jianhong_wu 发表于 2016-7-17 14:09 | 显示全部楼层
第二十七节:整除求商。

【27.1   什么叫整除?】

       最小的细分单位是“1”的除法运算就是整除,“1”不能再往下细分成小数点的除法运算就是整除。比如:
       10除以4,商等于2.5。------(带小数点,这个不是整除)
       10除以4,商等于2,余数是2。------(这才是整除)
       什么时候带小数点,什么时候是整除?取决于参与运算的变量类型。标准的C语言中,其实远远不止我前面所说的unsigned char ,unsigned int ,unsigned long这三种类型,比如还有一种叫浮点数类型的float,当参与运算的变量存在float类型时,就可能存在小数点。关于小数点的问题以后再讲,现在暂时不深入讲解,现在要知道的是,unsigned char ,unsigned int ,unsigned long这三种变量类型的除法都是属于整除运算,不带小数点的。

【27.2   整除的运算符号是什么样子的?】

       10除以4,商等于2,余数是2,这个整除的过程诞生了两个结果,一个是商,一个是余数,与此对应,整除就诞生出两个运算符号,你如果想计算结果返回商就用“整除求商”的符号“/”,你如果想计算结果返回余数就用“整除求余”的符号“%”。咋一看,整除运算中用到的两个符号“/”和“%”都不是我们日常生活中熟悉的除号“÷”,我个人猜测是因为“÷”这个符号在电脑键盘上不方便直接输入,因此C语言的语法规则选用“/”和“%”作为整除的运算符号。

【27.3   整除求商“/”。】

        整除求商的通用格式:
  1.         “保存变量”=“被除数” /  “除数1” /  “除数2”... /  “除数N”;

        跟之前讲的加减运算一样,赋值符号“=”左边的“保存变量”必须是变量,右边的可以是变量和常量的任意组合。如果右边只有两个参与运算的数据,就是整除求商的常见格式。
        整除求商的常见格式:
  1.         “保存变量”=“被除数” /  “除数” ;

        现在深入分析一下整除求商的运算规律。

       (1)当除数等于0时。
         我们都知道,数**算的除数是不允许等于0的,如果在51单片机中非要让除数为0,商会出现什么结果?我测试了一下,发现有一个规律:在unsigned char的变量类型下,如果“除数”是变量的0,商等于十进制的255(十六进制是0xff)。如果“除数”是常量的0,商等于十进制的1。比如:
  1.     unsigned char a;
  2.     unsigned char b;
  3.     unsigned char y=0;
  4.     a=23/y;  //除数变量y里面是0,那么a的结果是255(十六进制的0xff)。
  5.     b=23/0;  //除数是常量0,那么b的结果是1。

        平时做项目要尽量避免“除数是0”的情况,离它越远越好,但是既然除数不能为0,为什么我非要做“除数为0”时的实验呢?意义何在?这个实验的意义是,虽然我知道除数为0时会出错,但是我不知道这个错到底严不严重,会不会导致整个程序崩溃,当我做了这个实验后,我心中的石头才放下了,万一除数为0时,最多只是运算出错,但是不至于整个程序会崩溃,这样我心里就有了一个底,当哪天我某个程序崩溃跑飞时,我至少可以排除了“除数为0”这种情况,引导我从其它方面去找bug。

       (2)当被除数小于除数时。商等于0。比如:
  1.     unsigned char c;
  2.     c=7/10;   //c的结果是0。


        (3)当被除数等于除数时。商等于1。比如:
  1.     unsigned char d;
  2.     d=10/10;  //d的结果是1。


        (4)当被除数大于除数时。商大于0。比如:
  1.     unsigned char e;
  2.     unsigned char f;
  3.     e=10/4;  //e的结果是2,大于0。
  4.     f=10/3;  //f的结果是3,大于0。


【27.4   整除求商的自除简写。】

         当被除数是“保存变量”时,存在自除运算的简写。
  1.           “保存变量”=“保存变量” /  “除数” ;

         上述自除运算的简写如下:
  1.          “保存变量” / =“除数” ;

          比如:
  1.     unsigned char e;
  2.     g/=5;  //相当于g=g/5;


【27.5   整除求商有没有“自除1”的特殊写法?】

          加减法有自加1“++g”和自减1“g--”的特殊写法,但是除法不存在这种自除1的特殊写法,因为一个数除以1还是等于它本身,所以自除1没有任何意义,因此C语言语法中没有这种写法。

【27.6   整除求商的溢出。】

          除法的溢出规律跟加法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了避免一不小心就溢出的问题,我建议,不管加减乘除,凡是参与运算的变量全部都应该转化成unsigned long变量,转化的方法已经在前面章节讲过,不再重复讲解这方面的内容。

【27.7   例程练习和分析。】

          现在编写一个程序来验证刚才讲到的整除求商:
          程序代码如下:

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

  2. void main() //主函数
  3. {
  4.      unsigned char a;
  5.      unsigned char b;
  6.      unsigned char c;
  7.      unsigned char d;
  8.      unsigned char e;
  9.      unsigned char f;
  10.      unsigned char g=10;  //初始化为10
  11.      unsigned char y=0;   //除数变量初始化为0。

  12.      //(1)当除数等于0时。
  13.      a=23/y;
  14.      b=23/0;  //这行代码在编译时会引起一条警告“Warning”,暂时不用管它。

  15.      //(2)当被除数小于除数时。
  16.      c=7/10;

  17.      //(3)当被除数等于除数时。
  18.      d=10/10;

  19.      //(4)当被除数大于除数时。
  20.      e=10/4;
  21.      f=10/3;

  22.      //(5)整除求商的简写。
  23.      g/=5;  //相当于g=g/5;

  24.      View(a);              //把第1个数a发送到电脑端的串口助手软件上观察。
  25.      View(b);              //把第2个数b发送到电脑端的串口助手软件上观察。
  26.      View(c);              //把第3个数c发送到电脑端的串口助手软件上观察。
  27.      View(d);              //把第4个数d发送到电脑端的串口助手软件上观察。
  28.      View(e);              //把第5个数e发送到电脑端的串口助手软件上观察。
  29.      View(f);              //把第6个数f发送到电脑端的串口助手软件上观察。
  30.      View(g);              //把第7个数g发送到电脑端的串口助手软件上观察。

  31.      while(1)  
  32.      {
  33.      }
  34. }

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


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

  1. 开始...

  2. 第1个数
  3. 十进制:255
  4. 十六进制:FF
  5. 二进制:11111111

  6. 第2个数
  7. 十进制:1
  8. 十六进制:1
  9. 二进制:1

  10. 第3个数
  11. 十进制:0
  12. 十六进制:0
  13. 二进制:0

  14. 第4个数
  15. 十进制:1
  16. 十六进制:1
  17. 二进制:1

  18. 第5个数
  19. 十进制:2
  20. 十六进制:2
  21. 二进制:10

  22. 第6个数
  23. 十进制:3
  24. 十六进制:3
  25. 二进制:11

  26. 第7个数
  27. 十进制:2
  28. 十六进制:2
  29. 二进制:10


分析:        
          通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
whirt_noob 发表于 2016-7-23 16:16 | 显示全部楼层
后面还会有更新吗
 楼主| jianhong_wu 发表于 2016-7-24 11:06 | 显示全部楼层
本帖最后由 jianhong_wu 于 2016-7-24 11:20 编辑

第二十八节:整除求余。

【28.1   整除求余“%”。】

       上一节讲到,求商求余都是属于整除运算,区别是:求商返回商,求余返回余,求商是“/”,求余是“%”。求余的运算符号恰好就是我们平时常用的百分号“%”,之所以选择百分号作为求余的运算符号,我猜测是因为,在小于100%的数据中,如果我们仔细回味一下百分号的分子与分母的关系,其实就隐含了一层淡淡的求余的味道。

       整除求余的通用格式:
  1.        “保存变量”=“被除数”% “除数1” % “除数2”...%  “除数N”;

        跟之前讲的加减运算一样,赋值符号“=”左边的“保存变量”必须是变量,右边的可以是变量和常量的任意组合。如果右边只有两个参与运算的数据,就是整除求余的常见格式。
       整除求余的常见格式:
  1.        “保存变量”=“被除数” % “除数” ;      

       现在深入分析一下整除求余的运算规律。

     (1)当除数等于0时。
       我们都知道,数**算除数是不允许等于0的,如果在单片机中非要让除数为0,余数会出现什么结果?我在keil的C51编译环境试过,发现有一个规律:如果除数是变量的0,那么余数等于被除数。如果除数是常量的0,那么余数等于1。还有一种特殊的情况是编译不通过的,这种情况是“当被除数是变量,而除数是常量的0”。比如:
  1. unsigned char a;
  2. unsigned char b;
  3. unsigned char k=10;
  4. unsigned char y=0; //除数初始化为0

  5. a=23%y;  //除数变量y里面是0,a的结果等于被除数23。
  6. b=23%0;  //除数是常量0,b的结果是1。
  7. b=k%0;   //这种特殊情况编译不通过:被除数是变量,而除数是常量的0。

       平时做项目要尽量避免“除数是0”的情况,离它越远越好,但是既然除数不能为0,为什么我非要做“除数为0”时的实验呢?意义何在?这个实验的意义是,虽然我知道除数为0时会出错,但是我不知道这个错到底严不严重,会不会导致整个程序崩溃,当我做了这个实验后,我心中的石头才放下了,万一除数为0时,最多只是运算出错,但是不至于整个程序会崩溃,这样我心里就有了一个底,当哪天我某个程序崩溃跑飞时,我至少可以排除了“除数为0”这种情况,引导我从其它方面去找bug。

      (2)当被除数小于除数时。余数等于被除数本身。比如:
  1.     unsigned char c;
  2. c=7%10;  //c的结果是7。


      (3)当被除数等于除数时。余数等于0。比如:
  1.     unsigned char d;
  2. d=10%10;  //d的结果是0。


      (4)当被除数大于除数时。余数必然小于除数。比如:
  1. unsigned char e;
  2.     unsigned char f;
  3. e=10%4;  //e的结果是2。
  4. f=10%3;  //f的结果是1。


      (5)当除数等于1时。余数必然等于0。
  1.     unsigned char g;
  2. g=7%1;  //g的结果是0。


【28.2   整除求余的自除简写。】

        当被除数是“保存变量”时,存在自除求余的简写。
  1.         “保存变量”=“保存变量” %  “除数” ;

        上述自除求余的简写如下:
  1.         “保存变量” % =“除数” ;

         比如:
  1.     unsigned char h=9;
  2.     h%=5;  //相当于h=h%5; 最后余数的计算结果是4。


【28.3   整除求余有没有“自除1”的特殊写法?】

        加减法有自加1“++g”和自减1“g--”的特殊写法,但是求余的除法不存在这种自除1的特殊写法,因为任何一个数除以1的余数必然等于0,所以求余的自除1没有任何意义,因此C语言语法中没有这种特殊写法。

【28.4   整除求余的溢出。】

        不管是求商还是求余,除法的溢出规律跟加法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了避免一不小心就溢出的问题,我建议,不管加减乘除,凡是参与运算的变量全部都应该转化成unsigned long变量,转化的方法已经在前面章节讲过,不再重复讲解这方面的内容。

【28.5   例程练习和分析。】

        现在编写一个程序来验证刚才讲到的整除求余:
        程序代码如下:
  1. /*---C语言学习区域的开始。-----------------------------------------------*/

  2. void main() //主函数
  3. {
  4.      unsigned char a;
  5.      unsigned char b;
  6.      unsigned char c;
  7.      unsigned char d;
  8.      unsigned char e;
  9.      unsigned char f;
  10.      unsigned char g;  
  11.      unsigned char h=9;  //初始化为9。

  12.      unsigned char k=10;  //初始化为10。
  13.      unsigned char y=0; //除数变量初始化为0。

  14.          //(1)当除数等于0时。
  15.      a=23%y;
  16.      b=23%0;
  17.    //  b=k%0;  //这种特殊情况编译不通过:“被除数”是变量,而“除数”是常量的0。

  18.          //(2)当被除数小于除数时。
  19.      c=7%10;

  20.          //(3)当被除数等于除数时。
  21.      d=10%10;

  22.          //(4)当被除数大于除数时。
  23.      e=10%4;
  24.      f=10%3;

  25.          //(5)当除数等于1时。
  26.      g=7%1;

  27.          //(6)自除求余的简写。
  28.      h%=5;  //相当于h=h%5;

  29.      View(a);              //把第1个数a发送到电脑端的串口助手软件上观察。
  30.      View(b);              //把第2个数b发送到电脑端的串口助手软件上观察。
  31.      View(c);              //把第3个数c发送到电脑端的串口助手软件上观察。
  32.      View(d);              //把第4个数d发送到电脑端的串口助手软件上观察。
  33.      View(e);              //把第5个数e发送到电脑端的串口助手软件上观察。
  34.      View(f);              //把第6个数f发送到电脑端的串口助手软件上观察。
  35.      View(g);              //把第7个数g发送到电脑端的串口助手软件上观察。
  36.      View(h);              //把第8个数h发送到电脑端的串口助手软件上观察。

  37.      while(1)  
  38.      {
  39.      }
  40. }

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


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

  1. 开始...

  2. 第1个数
  3. 十进制:23
  4. 十六进制:17
  5. 二进制:10111

  6. 第2个数
  7. 十进制:1
  8. 十六进制:1
  9. 二进制:1

  10. 第3个数
  11. 十进制:7
  12. 十六进制:7
  13. 二进制:111

  14. 第4个数
  15. 十进制:0
  16. 十六进制:0
  17. 二进制:0

  18. 第5个数
  19. 十进制:2
  20. 十六进制:2
  21. 二进制:10

  22. 第6个数
  23. 十进制:1
  24. 十六进制:1
  25. 二进制:1

  26. 第7个数
  27. 十进制:0
  28. 十六进制:0
  29. 二进制:0

  30. 第8个数
  31. 十进制:4
  32. 十六进制:4
  33. 二进制:100


分析:        
         通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
梦娇 发表于 2016-7-25 14:33 | 显示全部楼层
女孩子能学会吗
男人怕胖 发表于 2016-7-25 16:13 | 显示全部楼层
liusiyi621 发表于 2016-7-25 22:10 | 显示全部楼层
有这种前辈真是后来者之福啊!感谢楼主无私奉献!
 楼主| jianhong_wu 发表于 2016-7-31 11:59 | 显示全部楼层
第二十九节:“先余后商”和“先商后余”提取数据某位,哪家强?

【29.1   先余后商。】

       求商求余除了数**算外,在实际单片机项目中还有一个很常用的功能,就是提取某个数的个十百千位。提取这些位有什么用呢?用途可大了,几乎凡是涉及界面显示的项目都要用到,比如数码管的显示,液晶屏的显示。提取某个数的个十百千位是什么意思呢?比如8562这个数,提取处理后,就可以得到千位的8,百位的5,十位的6,个位的2。这里提到的“个,十,百,千”位只是一个虚数,具体是多少应该根据实际项目而定,也有可能是“个,十,百,千,万,十万,百万...”等位,总之,提取的思路和方法都是一致的。下面以8562这个数为例开始介绍提取的思路和方法。

       第一步:先把8562拆分成8562,562,62,2这四个数。怎么拆分呢?用求余的算法。比如:
  1. 8562等于8562%10000;
  2. 562等于8562%1000;
  3. 62等于8562%100;
  4. 2等于8562%10;


       第二步:再从8562,562,62,2这四个数中分别提取8,5,6,2这四个数。怎么提取呢?用求商的算法。比如:
  1. 8等于8562/1000;
  2. 5等于562/100;
  3. 6等于62/10;
  4. 2等于2/1;


       第三步:最后,把第一步和第二步的处理思路连写在一起如下:
  1. 8等于8562%10000/1000;
  2. 5等于8562%1000/100;
  3. 6等于8562%100/10;
  4. 2等于8562%10/1;


       仔细观察,上述处理思路的规律感特别清晰,我们很容易发现其中的规律和原因,如果要提取“万,十万,百万...”的位数,也是用一样的思路。另外,多说一句,根据我的经验,有一些单片机的C编译器可能不支持long类型数据的求余求商连写在一起,那么就要分两步走“先求余,再求商”,分开来操作。比如:
  1.        unsigned char a;
  2.        a=8562%10000/1000;  //提取千位。

       分成两步走之后如下:
  1.        unsigned char a;
  2.        a=8562%10000;     
  3.        a=a/1000;          //提取千位。


      提取其它位分两步走的思路也是一样,不多说。


【29.2   先商后余。】

        刚才讲到了“先余后商”的提取思路,其实也可以倒过来“先商后余”,也就是先求商再求余数。下面还是以8562这个数为例。

        第一步:先把8562拆分成8,85,856,8562这四个数。怎么拆分呢?用求商的算法。比如:
  1. 8等于8562/1000;
  2. 85等于8562/100;
  3. 856等于8562/10;
  4. 8562等于8562/1;


        第二步:再从8,85,856,8562这四个数中分别提取8,5,6,2这四个数。怎么提取呢?用求余的算法。比如:
  1. 8等于8%10;
  2. 5等于85%10;
  3. 6等于856%10;
  4. 2等于8562%10;


         第三步:最后,把第一步和第二步的处理思路连写在一起如下:
  1. 8等于8562/1000%10;
  2. 5等于8562/100%10;
  3. 6等于8562/10%10;
  4. 2等于8562/1%10;


       上述的规律感也是特别清晰的。

【29.3   “先余后商”和“先商后余”哪家强?】

       上面讲了“先余后商”和“先商后余”这两种思路,到底哪种思路在实际项目中更好呢?其实我个人倾向于后者的“先商后余”,为什么呢?请看这个例子,以3100000000这个数为例,要提取该数的“十亿”位3。

       第一种:用“先余后商”的套路如下:
  1. 3等于3100000000%10000000000/1000000000;

       这里出现了一个问题,我们知道,unsigned long类型最大的数据是0xffffffff,转换成十进制后最大的数是4294967295,但是上面出现的10000000000这个数比unsigned long类型最大的数据4294967295还要大,这个就会引来我个人的担忧,C编译器到底会怎么处理,很有可能会出现意想不到的错误,至少会让我感到心里不踏实。当然,也许会有一些朋友说,这个是多虑的,最高位完全可以把求余这一步省略,这个说法也对,但是作为一种“套路”,我还是喜欢“套路”的对称感,“套路”之所以成为“套路”,是因为有一种对称感。下面再看看如果用“先商后余”的思路来处理,会不会出现这个担忧。

       第二种:用“先商后余”的套路如下:
  1. 3等于3100000000/1000000000%10;

       这一次,上面出现的1000000000这个数比unsigned long类型最大的数据4294967295小,所以没有刚才那种担忧,也维护了“套路”的对称感。所以我在实际项目中喜欢用这种方法。

【29.4   例程练习和分析。】

       现在编写一个程序来验证刚才讲到的两种思路:
       程序代码如下:
  1. /*---C语言学习区域的开始。-----------------------------------------------*/

  2. void main() //主函数
  3. {
  4.      unsigned char a; //千位
  5.      unsigned char b; //百位
  6.      unsigned char c; //十位
  7.      unsigned char d; //个位

  8.      unsigned char e; //千位
  9.      unsigned char f; //百位
  10.      unsigned char g; //十位  
  11.      unsigned char h; //个位  

  12.      //x初始化为8562,必须是unsignd int类型以上,不能是char类型,char最大范围是255。
  13.      unsigned int  x=8562;  //被提取的数

  14.          //第一种:先余后商。
  15.          a=x%10000/1000;  //提取千位
  16.          b=x%1000/100;    //提取百位
  17.          c=x%100/10;      //提取十位
  18.          d=x%10/1;        //提取个位

  19.          //第二种:先商后余。
  20.          e=x/1000%10;     //提取千位
  21.          f=x/100%10;      //提取百位
  22.          g=x/10%10;       //提取十位
  23.          h=x/1%10;        //提取个位

  24.      View(a);              //把第1个数a发送到电脑端的串口助手软件上观察。
  25.      View(b);              //把第2个数b发送到电脑端的串口助手软件上观察。
  26.      View(c);              //把第3个数c发送到电脑端的串口助手软件上观察。
  27.      View(d);              //把第4个数d发送到电脑端的串口助手软件上观察。
  28.      View(e);              //把第5个数e发送到电脑端的串口助手软件上观察。
  29.      View(f);              //把第6个数f发送到电脑端的串口助手软件上观察。
  30.      View(g);              //把第7个数g发送到电脑端的串口助手软件上观察。
  31.      View(h);              //把第8个数h发送到电脑端的串口助手软件上观察。

  32.      while(1)  
  33.      {
  34.      }
  35. }

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


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

  2. 第1个数
  3. 十进制:8
  4. 十六进制:8
  5. 二进制:1000

  6. 第2个数
  7. 十进制:5
  8. 十六进制:5
  9. 二进制:101

  10. 第3个数
  11. 十进制:6
  12. 十六进制:6
  13. 二进制:110

  14. 第4个数
  15. 十进制:2
  16. 十六进制:2
  17. 二进制:10

  18. 第5个数
  19. 十进制:8
  20. 十六进制:8
  21. 二进制:1000

  22. 第6个数
  23. 十进制:5
  24. 十六进制:5
  25. 二进制:101

  26. 第7个数
  27. 十进制:6
  28. 十六进制:6
  29. 二进制:110

  30. 第8个数
  31. 十进制:2
  32. 十六进制:2
  33. 二进制:10


分析:        
        通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
sunhq02 发表于 2016-8-2 09:27 | 显示全部楼层
虽然楼主精神可嘉, 但
这是吃力不讨好的事情
也不是真正的开源
datouyuan 发表于 2016-8-2 10:28 | 显示全部楼层
必须支持楼主!!!!
 楼主| jianhong_wu 发表于 2016-8-8 17:08 | 显示全部楼层
本帖最后由 jianhong_wu 于 2016-8-8 17:10 编辑

第三十节:逻辑运算符的“与”运算。

【30.1   “与”运算。】

      不管是十进制还是十六进制,单片机底层的运算都是以二进制的形式进行的,包括前面章节的加减乘除运算,在单片机的底层处理也是以二进制形式进行。只不过加减乘除我们平时太熟悉了,以十进制的形式口算或者笔算也能得到正确的结果,所以不需要刻意把十进制的数据先转换成二进制,然后再模拟单片机底层的二进制运算。但是本节的逻辑“与”运算,在分析它的运算过程和规律的时候,必须把所有的数据都转化成二进制才能进行分析,因为它强调的是二进制的位与位之间的逻辑运算。我们知道,二进制中的每一位只能是0或者1,两个数的“与”运算就是两个数被展开成二进制后的位与位之间的逻辑“与”运算。
      “与”运算的运算符号是“&”。运算规律是:两个位进行“与”运算,只有两个位都同时是1运算结果才能等于1,,否则,只要其中有一位是0,运算结果必是0.比如:
  1.       0&0等于0。
  2.       0&1等于0。
  3.       1&0等于0。
  4.       1&1等于1。


      注意,上述的0和1都是指二进制的0和1。


      现在举一个完整的例子来分析“与”运算的规律。有两个unsigned char类型的十进制数分别是12和9,求12&9的结果是多少?分析步骤如下:

      第一步:先把参与运算的两个数以二进制的格式展开。十进制转二进制的方法请参考前面第14,15,16节的内容。
  1.       十进制12的二进制格式是:00001100。
  2.       十进制9的二进制格式是: 00001001。



      第二步:二进制数右对齐,按上下每一位进行“与”运算。
  1.       十进制的12       ->     00001100   
  2.       十进制的9        ->    &00001001
  3.       “与”运算结果是  ->    00001000



       第三步:把二进制的00001000转换成十六进制是:0x08。转换成十进制是8。所以12&9的结果是8。

       上述举的例子只能分析“与”运算的规律,并没有看出“与”运算的意义所在。“与”运算有啥用途呢?其实用途很多,最常见的用途是可以指定一个变量二进制格式的某位清零,其它位保持不变。比如一个unsigned char类型的变量b,数据长度一共是8位,从右往左:
       想让第0位清零,其它位保持不变,只需跟十六进制的0xfe相“与”:b=b&0xfe。
       想让第1位清零,其它位保持不变,只需跟十六进制的0xfd相“与”:b=b&0xfd。
       想让第2位清零,其它位保持不变,只需跟十六进制的0xfb相“与”:b=b&0xfb。
       想让第3位清零,其它位保持不变,只需跟十六进制的0xf7相“与”:b=b&0xf7。
       想让第4位清零,其它位保持不变,只需跟十六进制的0xef相“与”:b=b&0xef。
       想让第5位清零,其它位保持不变,只需跟十六进制的0xdf相“与”:b=b&0xdf。
       想让第6位清零,其它位保持不变,只需跟十六进制的0xbf相“与”:b=b&0xbf。
       想让第7位清零,其它位保持不变,只需跟十六进制的0x7f相“与”:b=b&0x7f。
       根据上述规律,假设b原来等于十进制的85(十六进制是0x55,二进制是01010101),要想把此数据的第0位清零,只需b=b&0xfe。最终b的运算结果是十进制是84(十六进制是0x54,二进制是01010100)。把它们展开成二进制格式的运算过程如下:
  1.       十进制的85       ->     01010101   
  2.       十六进制的0xfe   ->    &11111110
  3.        “与”运算结果是  ->     01010100



【30.2   例程练习和分析。】

    现在编写一个程序来验证刚才讲到的“与”运算:
程序代码如下:

  1. /*---C语言学习区域的开始。-----------------------------------------------*/
  2. void main() //主函数
  3. {
  4.      unsigned char a;
  5.      unsigned char b=85;  //十六进制是0x55,二进制是01010101。
  6.      a=12&9;
  7.      b=b&0xfe;   
  8.      View(a);              //把第1个数a发送到电脑端的串口助手软件上观察。
  9.      View(b);              //把第2个数b发送到电脑端的串口助手软件上观察。
  10.      while(1)  
  11.      {
  12.      }
  13. }
  14. /*---C语言学习区域的结束。-----------------------------------------------*/

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


  1. 开始...
  2. 第1个数
  3. 十进制:8
  4. 十六进制:8
  5. 二进制:1000



  6. 第2个数
  7. 十进制:84
  8. 十六进制:54
  9. 二进制:1010100



分析:        
      通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
minDragon 发表于 2016-8-8 22:41 | 显示全部楼层
鸿哥威武
wupin12345 发表于 2016-8-9 15:14 | 显示全部楼层
前辈辛苦,有没有模数转换显示的程序范例
杉侠 发表于 2016-8-11 16:21 | 显示全部楼层
 楼主| jianhong_wu 发表于 2016-8-14 13:50 | 显示全部楼层
第三十一节:逻辑运算符的“或”运算。

【31.1   “或”运算。】

      “或”运算也是以位为单位进行运算的。位是指二进制中的某一位,位只能是0或者1。两个数的“或”运算就是转换成二进制后每一位的“或”运算。
      “或”运算的符号是“|”。运算规律是:两个位的“或”运算,如果两个位都是0,那么运算结果才是0,否则只要其中有一位是1,那么运算结果必定是1。比如:
  1.       0|0等于0。
  2.       0|1等于1。
  3.       1|0等于1。
  4.       1|1等于1。


      现在举一个完整的例子来分析“|”运算的规律。有两个unsigned char类型的十进制数分别是12和9,求12|9的结果是多少?分析步骤如下:

      第一步:先把参与运算的两个数以二进制的格式展开。十进制转二进制的方法请参考前面第13,14,15节的内容。
  1.       十进制12的二进制格式是:00001100。
  2.       十进制9的二进制格式是: 00001001。



      第二步:二进制数右对齐,按上下每一位进行“或”运算。
  1.       十进制的12       ->     00001100   
  2.       十进制的9        ->    |00001001
  3.       “或”运算结果是  ->    00001101



      第三步:把二进制的00001101转换成十六进制是:0x0D。转换成十进制是13。所以12|9的结果是13。

      上一节讲的“与”运算最常见的用途是可以指定一个变量的某位清0,而本节的“或”运算刚好相反,“或”运算最常见的用途是可以指定一个变量的某位置1,其它位保持不变。比如一个unsigned char类型的变量b,数据长度一共是8位,从右往左:
       想让第0位置1,其它位保持不变,只需跟十六进制的0x01相“或”:b=b|0x01。
       想让第1位置1,其它位保持不变,只需跟十六进制的0x02相“或”:b=b|0x02。
       想让第2位置1,其它位保持不变,只需跟十六进制的0x04相“或”:b=b|0x04。
       想让第3位置1,其它位保持不变,只需跟十六进制的0x08相“或”:b=b|0x08。
       想让第4位置1,其它位保持不变,只需跟十六进制的0x10相“或”:b=b|0x10。
       想让第5位置1,其它位保持不变,只需跟十六进制的0x20相“或”:b=b|0x20。
       想让第6位置1,其它位保持不变,只需跟十六进制的0x40相“或”:b=b|0x40。
       想让第7位置1,其它位保持不变,只需跟十六进制的0x80相“或”:b=b|0x80。
       根据上述规律,假设b原来等于十进制的84(十六进制是0x54,二进制是01010100),要想把此数据的第0位置1,只需b=b|0x01。最终b的运算结果是十进制是85(十六进制是0x55,二进制是01010101)。把它们展开成二进制格式的运算过程如下:
  1.        十进制的84       ->     01010100   
  2.        十六进制的0x01   ->    |00000001
  3.        “或”运算结果是  ->     01010101



【31.2   例程练习和分析。】

       现在编写一个程序来验证刚才讲到的“或”运算:
程序代码如下:
  1. /*---C语言学习区域的开始。-----------------------------------------------*/

  2. void main() //主函数
  3. {
  4.      unsigned char a;
  5.      unsigned char b=84;  //十六进制是0x54,二进制是01010100。

  6.      a=12|9;
  7.      b=b|0x01;   

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

  10.      while(1)  
  11.      {
  12.      }
  13. }

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



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

  1. 开始...

  2. 第1个数
  3. 十进制:13
  4. 十六进制: D
  5. 二进制:1101

  6. 第2个数
  7. 十进制:85
  8. 十六进制:55
  9. 二进制:1010101



分析:        
       通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
damoyeren 发表于 2016-8-15 09:25 | 显示全部楼层
持续关注,希望写书的时候从工程实际的角度出发。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部