独闷闷网

 找回密码
 立即注册
搜索
楼主: jianhong_wu
打印 上一主题 下一主题
收起左侧

[原创] 从业十年,教你单片机入门基础。(连载)

[复制链接]
72#
 楼主| 发表于 2015-9-20 06:06:46 | 只看该作者
本帖最后由 jianhong_wu 于 2015-9-20 06:11 编辑

第三十七节:if判断语句以及常量变量真假的判断。
        “if”是C语言的判断语句关键词,意思是如果if小括号里面的条件满足,就执行条件后面大括号里的语句;如果条件不满足,则直接跳过条件后面大括号里的语句。“if”语句的常见格式如下:
if(条件)
{
    语句1;
    语句2;
    语句3;
……
}
        还有一种省略大括号的书写格式,但是要注意,当if条件语句后面省略了大括号时,如果if小括号里面的条件满足,仅仅执行条件后面第一条语句,如果条件不满足,则跳过条件后面第一条语句。比如:
if(条件)
    语句1;
    语句2;
    语句3;
……
       上述格式省略了大括号,实际上默认相当于:
if(条件)
{
    语句1;
}
    语句2;
    语句3;
……
       上述语句分析:当条件满足时,就执行语句1,如果不满足,就跳过语句1,直接从语句2处开始往后执行。在实际项目中,为了阅读清晰,建议大家不要省略大括号。
        接着讲另一个新的知识点,对于if(条件),if语句的条件包含两种,一种是常量或者变量真假的判断,另一种是关系判断。本节内容先举例讲常量或变量的判断。比如:
if(常量或者变量)
{
    语句1;
    语句2;
}
    语句3;
    语句4;
……
        当小括号里面的常量或者变量大于0时,就代表小括号里面的条件满足;当小括号里面的常量或者变量等于0时,就代表小括号里面的条件不满足。还有一种专业的说法,条件满足称之为“真”,条件不满足称之为“假”。在这里,常量或者变量大于0称之为“真”,等于0称之为“假”。还可以换一种思路来记忆,常量或者变量不等于0称之为“真”,等于0称之为“假”。比如刚才的例子:
if(常量或者变量)
{
    语句1;
    语句2;
}
    语句3;
    语句4;
……
        若条件为真,则从语句1处开始执行,若条件为假,则跳过语句1和语句2,直接从语句3开始执行。
        现在编写一个程序,有5条if判断语句,如果条件为真,累加统计变量就会自动加1,最后看看条件为真的语句有几条。最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char x=2;
  6.   unsigned char y=0;
  7.   unsigned char a=0;  //此变量统计有多少条语句是真的

  8.   if(1)      //常量大于0,因此为真
  9.   {
  10.      a=a+1;  //a由0自加1后变成1。
  11.   }


  12.   if(0)   //常量等于0,因此为假
  13.   {
  14.      a=a+1;  //由于条件为假,这条语句没有被执行,因此此时a仍然是1
  15.   }


  16.   if(15)     //常量大于0,因此为真
  17.   {
  18.      a=a+1;  //a由1自加1后变成2。
  19.   }



  20.   if(x)     //变量x为2,大于0,因此为真
  21.   {
  22.      a=a+1;  //a由,2自加1后变成3。
  23.   }


  24.   if(y)     //变量y为0,等于0,因此为假
  25.   {
  26.      a=a+1;  //由于条件为假,这条语句没有被执行,因此此时a仍然是3
  27.   }
  28.         
  29.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示

  30.         
  31. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  32.    while(1)  
  33.    {
  34.       initial();
  35.       key_service();
  36.       display_service();
  37.    }

  38. }
复制代码


         查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0。上坚鸿51学习板观察程序执行的结果如下:
         变量a为3。
         下节预告:等于关系符“==”和不等于关系符“!=”。
(未完待续)

乐于分享,勇于质疑!
71#
 楼主| 发表于 2015-9-13 08:47:46 | 只看该作者
本帖最后由 jianhong_wu 于 2015-9-13 08:48 编辑

第三十六节:括号改变优先级。
        C语言的加减乘除,与或取反,左移右移等运算符是有严格优先级顺序的,但是我本人记忆力有限,做项目哪能记住这么多优先级的前后顺序,只是大概明白乘除的优先级比加减的优先级高,其它方面真的记不住那么多,怎么办?为了确保万一,我用到了括号。
       括号的用法跟我们日常的数据运算公式的用法一直,先运行括号里面的运算,再执行其它运算。比如:
a=a<<2+5;
       到底是先把变量a左移2位后再加5,还是先2加5等于7再让变量a左移7位?对于像我这样不能熟记C语言运算优先级顺序的人,这条语句很容易让我搞混。但是加上括号就明了:
a=(a<<2)+5;
a=a<<(2+5);
      不用多说,加上括号后,上述两行代码传递了清晰的优先级顺序。再看一个例子:
c=1+3*c;
      到底是1加3的结果再乘以变量c,还是3乘以变量c的结果再加1?因为我记得乘除法的优先级比加减法的优先级高,所以答案是3乘以变量c的结果再加1。对于初学者,为了避免出错,可以加上括号就更加清晰了,比如:
c=(1+3)*c;
c=1+(3*c);
      加括号后,优先级顺序一目了然。

      现在编写一个程序来练习刚才讲到的主要内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a=0x01;
  6.   unsigned char b=0x01;

  7.   unsigned char c=0x02;
  8.   unsigned char d=0x02;


  9.   a=(a<<2)+5;  //a左移2位后变成4,再加5等于9
  10.   b=b<<(2+5);  //2加5等于7,b再左移动7位等于128

  11.   c=(1+3)*c;  //1加3等于4,再乘以变量c等于8
  12.   d=1+(3*d);  //3乘以d等于6,再加1等于7

  13.         
  14.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  15.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示
  16.   GuiWdData2=c;   //把c这个变量放到窗口变量2里面显示
  17.   GuiWdData3=d;   //把d这个变量放到窗口变量3里面显示

  18.         
  19. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  20.    while(1)  
  21.    {
  22.       initial();
  23.       key_service();
  24.       display_service();
  25.    }

  26. }
复制代码


        查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0。上坚鸿51学习板观察程序执行的结果如下:
       变量a为9。
       变量b为128。
       变量c为8。
       变量d为7。
      下节预告:if判断语句和等于关系符“==”。
     (未完待续)

乐于分享,勇于质疑!
70#
 楼主| 发表于 2015-9-6 06:56:56 | 只看该作者
本帖最后由 jianhong_wu 于 2015-9-6 06:59 编辑

第三十五节:移位运算的右移。
        右移的运算符号是“>>”,语法格式如下:
        保存变量=被移数>>n;
        其中n代表“被移数”需要右移的位数。
        整句语法的含义是:“被移数”的二进制格式数据被整体往右边移动了n位,原来低n位数据被直接覆盖,新空出的高n位数据填入0。最后把移位结果存入“保存变量”。
        现在举一个完整的例子来分析“右移”运算的规律。有2个unsigned char类型的变量a和b,a的初始值是十进制数5,a=a>>1的结果是多少?b的初始值也是十进制数5,b=b>>2的结果是多少?
分析步骤如下:
        第一步:先把参与运算的数转换成二进制的格式。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制5的二进制格式是:00000101。
       第二步:
(1) 将5的二进制数整体往右边移动1位:
                                   原来是:00000101
        整体往右移动1位后变成:00000010
        把二进制的00000010转换成十六进制是:0x02。转换成十进制是2。所以a初始值是5, 右移1位后的结果是2.
(2) 将5的二进制数整体往右边移动2位:
                                   原来是:00000101
        整体往右移动2位后变成:00000001
        把二进制的00000001转换成十六进制是:0x01。转换成十进制是1。所以b初始值是5, 右移2位后的结果是1。
       上一节讲的“左移”1位有乘以2的规律,相反,这节讲的“右移”也存在整除的规律:某数右移1位相当于此数整除2,右移多少位相当于整除多少个2.比如上述例子中5右移1位相当于5整除2,结果等于2。而5右移2位相当于5整除2再整除2,5/2/2的结果等于1。既然右移1位相当于某个数整除2,那么为什么不直接用整除来替代右移呢?原因是一条右移语句的运算速度比一条整除语句的运算速度要快很多倍。
        右移是在单片机项目中很常用的语法,也经常应用在一些数据类型之间的拆分中。比如有一个双字节unsigned int类型的变量c,它的初始值是0x1234,要把它拆分成两个unsigned char单字节的类型数据H和L,其中H是高8位字节,L是低八位字节,拆分后H应该等于0x12,L应该等于0x34,此程序如何写?就需要用到右移。程序分析如下:
unsigned char H;  //单字节
unsigned char L;  //单字节
unsigned int c=0x1234; //双字节
L=c;  //c的低8位直接赋值给单字节的L
H=c>>8;  //c先把高8位右移到低8位,然后再把这8位数据赋值给H
        程序运行结果:H就等于十六进制的0x12,十进制是18。L就等于十六进制的0x34,十进制是52.提一个问题,请问执行完上述最后一条语句H=c>>8后,此时c的值是多少?答案是0x1234,因为只要它没有赋值给它自己,执行完语句后就不会改变它自己本身。
       再多讲一下知识点,右移也存在简写格式,比如:
e>>=1; //就相当于e=e>>1;
f>>=2; //就相当于f=f>>2;
       现在编写一个程序来练习刚才讲到的主要内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:


  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/

  4. unsigned char a=5;
  5. unsigned char b=5;


  6. unsigned char H; //单字节
  7. unsigned char L; //单字节
  8. unsigned int c=0x1234; //双字节

  9. unsigned int d;

  10. a=a>>1;
  11. b=b>>2;

  12. L=c; //c的低8位直接赋值给单字节的L
  13. H=c>>8; //c先把高8位右移到低8位,然后再把这8位数据赋值给H

  14. //执行上述语句后,此时的c变量的数值是多少呢?
  15. //答案是0x1234,因为只要没有赋值给它自己,就不会改变它自己.

  16. d=c; //此时d就等于c,是十六进制的0x1234.十进制是4660

  17. GuiWdData0=a; //把a这个变量放到窗口变量0里面显示
  18. GuiWdData1=b; //把b这个变量放到窗口变量1里面显示
  19. GuiWdData2=H; //把H这个变量放到窗口变量2里面显示
  20. GuiWdData3=L; //把L这个变量放到窗口变量3里面显示
  21. GuiWdData4=d; //把d这个变量放到窗口变量4里面显示

  22. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  23. while(1)
  24. {
  25. initial();
  26. key_service();
  27. display_service();
  28. }

  29. }

复制代码

        查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0。上坚鸿51学习板观察程序执行的结果如下:
       变量a为2 (十六进制是0x02,二进制是00000010)。
       变量b为1(十六进制是0x01,二进制是00000001)。
       变量H为18(十六进制是0x12,二进制是00010010)。
       变量L为52(十六进制是0x34,二进制是00110100)。
       变量d为4660(十六进制是0x1234,二进制是0001 0010 0011 0100)。

     下节预告:括号改变优先级。
(未完待续)



乐于分享,勇于质疑!
69#
 楼主| 发表于 2015-9-5 13:07:05 | 只看该作者
本帖最后由 jianhong_wu 于 2015-9-6 06:55 编辑

第三十四节:移位运算的左移。
        前面讲的“与,或,异或”运算,漏讲了它们的简写格式的内容,在本节开始之前先把这部分内容补上。在“与,或,异或”这些运算中,当赋值语句左边的“保存变量”是参与运算的变量本身时,也存在简写的语法格式,比如:
      a&=0x01;  //相当于a=a&0x01;
      a|=0x01;  //相当于a=a|0x01;
      a^=0x01;  //相当于a=a^0x01;

     现在正式开始本节内容“移位运算的左移”。左移的运算符号是“<<”,语法格式如下:
     保存变量=被移数<<n;
     其中n代表“被移数”需要左移的位数。
     整句语法的含义是:“被移数”的二进制格式数据被整体往左边移动了n位,原来高n位数据被直接覆盖,新空出的低n位数据填入0。最后把移位结果存入“保存变量”。
     现在举一个完整的例子来分析“左移”运算的规律。有2个unsigned char类型的变量a和b,a的初始值是十进制数5,a=a<<1的结果是多少?b的初始值也是十进制数5,b=b<<2的结果是多少?
     分析步骤如下:
     第一步:先把参与运算的数转换成二进制的格式。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制5的二进制格式是:00000101。
     第二步:
(1) 将5的二进制数整体往左边移动1位:
                                   原来是:00000101
        整体往左移动1位后变成:00001010
        把二进制的00001010转换成十六进制是:0x0A。转换成十进制是10。所以a初始值是5,左移1位后的结果是10.
(2) 将5的二进制数整体往左边移动2位:
                                  原来是:00000101
        整体往左移动2位后变成:00010100
        把二进制的00010100转换成十六进制是:0x14。转换成十进制是20。所以b初始值是5,左移2位后的结果是20.
        仔细观察上述两个例子,发现了一个重要的规律:某数左移1位相当于此数乘以2,左移多少位相当于乘以多少个2.比如上述例子中5左移1位相当于5乘以2,结果等于10。而5左移2位相当于5乘以2再乘以2,5*2*2的结果等于20。既然左移1位相当于某个数乘以2,那么为什么不直接用乘法来替代左移呢?原因是一条左移语句的运算速度比一条乘法语句的运算速度要快很多倍。
        左移是在单片机项目中很常用的语法,也经常应用在一些数据类型之间的合并中。比如有两个unsigned char单字节的类型数据H和L,H的初始值是十六进制的0x12,L的初始值是十六进制的0x34,要将两个单字节的H和L合并成一个unsigned int双字节的数据c,其中H是高8位字节,L是低八位字节,合并成c后,c的值应该是十六进制的0x1234,此程序如何写?就需要用到左移。程序分析如下:
unsigned char H=0x12;  //单字节
unsigned char L=0x34;  //单字节
unsigned int c; //双字节
c=H;//c的低8位被H覆盖,也就是c的低8位得到了H的各位值。
c=c<<8; //及时把c的低8位移动到高8位,同时c原来的低8位被填入0
c=c+L;//此时c再加L,c的低8位就L的值。
程序运行结果:c就等于十六进制的0x1234,十进制是4660。
       再多讲一下知识点,左移也存在简写格式,比如:
d<<=1; //就相当于d=d<<1;
e<<=2; //就相当于e=e<<2;
       现在编写一个程序来练习刚才讲到的主要内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:



  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/


  4. unsigned char a=5;
  5. unsigned char b=5;

  6. unsigned char H=0x12; //单字节
  7. unsigned char L=0x34; //单字节
  8. unsigned int c; //双字节

  9. a=a<<1; //a左移1位,从原来的5变成了10.
  10. b=b<<2; //b左移2位,从原来的5变成了20.


  11. c=H; //c的低8位被H覆盖,也就是此时c的低8位得到了H的各位值。
  12. c=c<<8; //及时把c的低8位移动到高8位,同时c原来的低8位被填入0
  13. c=c+L; //此时c再加L,c的低8位就L的值。此时c得到了H和L合并而来的值。


  14. GuiWdData0=a; //把a这个变量放到窗口变量0里面显示
  15. GuiWdData1=b; //把b这个变量放到窗口变量1里面显示
  16. GuiWdData2=c; //把c这个变量放到窗口变量2里面显示



  17. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  18. while(1)
  19. {
  20. initial();
  21. key_service();
  22. display_service();
  23. }

  24. }

复制代码

        查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0。上坚鸿51学习板观察程序执行的结果如下:
        变量a为10 (十六进制是0x0A,二进制是00001010)。
        变量b为20(十六进制是0x14,二进制是00010100)。
        变量c为4660(十六进制是0x1234,二进制是0001 0010 0011 0100)。
        下节预告:移位运算的右移。
(未完待续)



乐于分享,勇于质疑!
68#
 楼主| 发表于 2015-8-30 07:05:49 | 只看该作者
本帖最后由 jianhong_wu 于 2015-8-30 10:46 编辑

第三十三节:逻辑运算符的“按位取反”和“非”。
       “按位取反”运算的符号是波浪符号“~”。运算规律是:针对某个数的“按位取反”,先将其展开成二进制的格式,然后每个位取反,所谓取反就是1的变成0,0的变成1。
         现在举一个完整的例子来分析“按位取反”运算的规律。有一个unsigned char类型的十进制数分别是5和0,求~5和~0的结果是多少?分析步骤如下:
         第一步:先把参与运算的数转换成二进制的格式。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制5的二进制格式是:00000101。
十进制0的二进制格式是:00000000。
         第二步:
(1)将5的二进制数每一位取反,1的变成0,0的变成1。
        ~00000101
结果是11111010。
把二进制的11111010转换成十六进制是:0xFA。转换成十进制是250。所以~5的结果是250。
(2)将0的二进制数每一位取反,1的变成0,0的变成1。
        ~00000000
结果是11111111。
把二进制的11111111转换成十六进制是:0xFF。转换成十进制是255。所以~0的结果是255。
       讲完“按位取反”,现在接着讲“非”。 “非”跟“按位取反”有点相似,但是区别也明显。“按位取反”针对的是一个数的某一位,侧重在局部。而“非”是针对一个数的整体,侧重在全局。“非”只有两种状态“假”和“真”。0代表假,大于0的数值代表真,也可以说“非”假即真,“非”真即假。不是假的就是真的,不是真的就是假的。强调的是两种状态的切换。在数值表示上,用0代表假的状态,用1代表真的状态。
      “非”运算的符号是感叹号“!”。运算规律是:针对某个数的“非”,不管此数有多大,只要它大于0,那么被“非”后就一定是0。也不管此数是什么变量类型,只要它数值等于0,那么被“非”后就一定是1。
        现在举一个完整的例子来分析“非”运算的规律。有一个unsigned char类型的十进制数分别是5和0,求!5和!0的结果是多少?分析步骤如下:
        第一步:5大于0,是一个整体,被“非”后为0.
        第二步:0就是0,是一个整体,被“非”后为1.

        现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         

  5.   unsigned char a=5;
  6.   unsigned char b=5;
  7.   unsigned char c=0;
  8.   unsigned char d=0;

  9.   a=~a;
  10.   b=!b;


  11.   c=~c;
  12.   d=!d;

  13.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  14.   GuiWdData1=b;   //把a这个变量放到窗口变量1里面显示
  15.   GuiWdData2=c;   //把a这个变量放到窗口变量2里面显示
  16.   GuiWdData3=d;   //把a这个变量放到窗口变量3里面显示   


  17. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  18.    while(1)  
  19.    {
  20.                   initial();
  21.       key_service();
  22.       display_service();
  23.    }

  24. }
复制代码


       查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0。上坚鸿51学习板观察程序执行的结果如下:
       变量a为250(十六进制是0xFA,二进制是11111010)。
       变量b为0 (十六进制是0x00,二进制是00000000)。
       变量c为255(十六进制是0xFF,二进制是11111111)。
       变量d为1 (十六进制是0x01,二进制是00000001)。
       下节预告:移位运算的左移。
(未完待续)

乐于分享,勇于质疑!
67#
 楼主| 发表于 2015-8-23 16:21:14 | 只看该作者
本帖最后由 jianhong_wu 于 2015-8-23 16:25 编辑

第三十二节:逻辑运算符的“异或”运算。
       “异或”运算是以位为单位进行运算的。位是指二进制中的某一位,位只能是0或者1。两个数的“异或”运算就是转换成二进制后每一位的“异或”运算。
       “异或”运算的符号是“^”。运算规律是:两个位的“异或”运算,如果两个位都相同,那么运算结果就是0;如果两个位不同(相异),则运算结果是1。比如:
0^0等于0。(两个位相同)
0^1等于1。(两个位相异)
1^0等于1。(两个位相异)
1^1等于0。(两个位相同)
       现在举一个完整的例子来分析“^”运算的规律。有两个unsigned char类型的十进制数分别是12和9,求12^9的结果是多少?分析步骤如下:
       第一步:先把参与运算的两个数都转换成二进制的格式。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制12的二进制格式是:00001100。
十进制9的二进制格式是:00001001。
       第二步:二进制数右对齐,按每一位进行“异或”运算。
            00001100
         ^00001001
结果是 00000101。
        第三步:把二进制的 00000101转换成十六进制是:0x05。转换成十进制是5。所以12^9的结果是5。
       “异或”在哪些项目上经常应用?以我个人的项目经验,平时很少用“异或”,我唯一用过一次“异或”,是在制定串口通讯协议时,通过“异或”算法,增加一个校验字节,此校验字节是一串数据依次相“异或”的总结果,目的是为了增加数据传送时的抗干扰能力。

       现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;


  6.   a=12^9;


  7.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示

  8.         
  9. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  10.    while(1)  
  11.    {
  12.       initial();
  13.       key_service();
  14.       display_service();
  15.    }

  16. }
复制代码


         查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0上坚鸿51学习板观察程序执行的结果如下:
        变量a为5(十六进制是0x05,二进制是00000101)。

        下节预告:逻辑运算符的“按位取反”运算。
(未完待续)

乐于分享,勇于质疑!
66#
 楼主| 发表于 2015-8-15 08:40:59 | 只看该作者
本帖最后由 jianhong_wu 于 2015-8-15 08:42 编辑

第三十一节:逻辑运算符的“或”运算。
       “或”运算是以位为单位进行运算的。位是指二进制中的某一位,位只能是0或者1。两个数的“或”运算就是转换成二进制后每一位的“或”运算。
       “或”运算的符号是“|”。运算规律是:两个位的“或”运算,如果两个位都是0,那么运算结果才是0,否则只要其中有一位是1,那么运算结果必定是1。比如:
0|0等于0。
0|1等于1。
1|0等于1。
1|1等于1。
      现在举一个完整的例子来分析“|”运算的规律。有两个unsigned char类型的十进制数分别是12和9,求12|9的结果是多少?分析步骤如下:
      第一步:先把参与运算的两个数都转换成二进制的格式。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制12的二进制格式是:00001100。
十进制9的二进制格式是:00001001。
      第二步:二进制数右对齐,按每一位进行“或”运算。
           00001100
          |00001001
结果是 00001101。
      第三步:把二进制的00001101转换成十六进制是:0x0D。转换成十进制是13。所以12&9的结果是13。
      “或”运算最常见的用途是可以指定一个变量的某位置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)。

       现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;
  6.   unsigned char b=84;  //十六进制是0x54,二进制是01010100。

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


  9.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  10.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示

  11.         
  12. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  13.    while(1)  
  14.    {
  15.       initial();
  16.       key_service();
  17.       display_service();
  18.    }

  19. }
复制代码


       查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0上坚鸿51学习板观察程序执行的结果如下:
      变量a为13(十六进制是0x0D,二进制是00001101)。
      变量b为85(十六进制是0x55,二进制是01010101)。
      下节预告:逻辑运算符的“异或”运算。
(未完待续)

乐于分享,勇于质疑!
65#
 楼主| 发表于 2015-8-8 11:19:38 | 只看该作者
本帖最后由 jianhong_wu 于 2015-8-8 11:44 编辑

第三十节:逻辑运算符的“与”运算。
       单片机任何数字的底层运算都是以二进制的形式进行,前面讲的加减乘除也不例外。只不过加减乘除是生活常用的运算,任意给两个十进制的数据让单片机运算,我们都可以凭借既有的生活经验通过口算或者笔算来计算出结果,不需要刻意模拟单片机底层的二进制运算。但是本节讲的“与”运算却不行,它是为二进制而生的,若想使用它,必先把参与运算的数据双双转换成二进制格式的数据,你才能分析“与”运算的含义和规律。“与”运算是以位来进行运算的,位就是代表二进制中的每一位,每一个位只能是0或者1。两个数的“与”运算就是两个数被展开成二进制后的“与”运算。
       “与”运算的运算符号是“&”。运算规律是:两个位进行“与”运算,只有两个位都同时是1运算结果才能等于1,,否则,只要其中有一位是0,运算结果都是0.比如:
0&0等于0。
0&1等于0。
1&0等于0。
1&1等于1。
注意,上述的0和1都是指二进制的0和1。
       现在举一个完整的例子来分析“与”运算的规律。有两个unsigned char类型的十进制数分别是12和9,求12&9的结果是多少?分析步骤如下:
       第一步:先把参与运算的两个数以二进制的格式展开。十进制转二进制的方法请参考前面第13,14,15节的内容。
十进制12的二进制格式是:00001100。
十进制9的二进制格式是:00001001。
       第二步:二进制数右对齐,按每一位进行“与”运算。
            00001100
          &00001001
结果是 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)。

       现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;
  6.   unsigned char b=85;  //十六进制是0x55,二进制是01010101。

  7.   a=12&9;
  8.   b=b&0xfe;   


  9.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  10.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示

  11.         
  12. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  13.    while(1)  
  14.    {
  15.       initial();
  16.       key_service();
  17.       display_service();
  18.    }

  19. }
复制代码


        查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。16个LED灯显示的就是当前变量的二进制数,亮代表1,灭代表0上坚鸿51学习板观察程序执行的结果如下:
        变量a为8(十六进制是0x08,二进制是00001000)。
        变量b为84(十六进制是0x54,二进制是01010100)。
        下节预告:逻辑运算符的“或”运算。
(未完待续)

乐于分享,勇于质疑!
64#
 楼主| 发表于 2015-8-1 15:13:28 | 只看该作者
第二十九节:利用“整除求商求余”来拆分提取一个数的个十百千位。
上两节讲了整除的求商求余运算符,这两个运算不仅能用在数学运算中,还可以用来拆分提取一个数的个十百千位。提取这些位有什么用呢?因为在以后的单片机显示程序中,不管是液晶屏还是数码管,必须用到这种提取算法,先把一个数的个十百千位一个个拆分提取出来,然后再送到显示屏上显示,所以这种算法很常见和实用。上述提到的“个,十,百,千”位只是一个虚数,具体是多少应该根据实际项目而定,也有可能是“个,十,百,千,万,十万,百万...”等位,但是处理的思路和方法都是一致的。
拆分提取的思路。比如8562这个数,千位是8,百位是5,十位是6,个位是2。可以依次看成是:
  1. 8=8562/1000;
  2. 5=562/100;
  3. 6=62/10;
  4. 2=2/1;
复制代码

上述用到了整除求商,但是562,62,2又是如何通过8562分解得到的呢?需要用到整除求余:
  1. 562=8562%1000;
  2. 62=8562%100;
  3. 2=8562%10;
复制代码

最后综合在一起,连在一起写:
  1. 8=8562/1000;
  2. 5=8562%1000/100;
  3. 6=8562%100/10;
  4. 2=8562%10/1;
复制代码

因为我们预先知道了这个数最大位是千位,所以千位直接整除1000求商就可以了。实际项目中,我们只是用某个变量,而这个变量的大小我们并不知道具体是什么,它的最大位可能并不止千位,也有可能是万位,所以需要把上述最高位的千位也做一下整除10000求余数,然后在整除1000求商,最后整理如下:
  1. 8=8562%10000/1000;
  2. 5=8562%1000/100;
  3. 6=8562%100/10;
  4. 2=8562%10/1;
复制代码

大家仔细观察和品味一下,很容易发现规律和原因,如果求万,十万,百万,也是用一样的方法。多提醒一句,根据我的经验,有一些单片机的C编译器可能不支持long类型数据的求余求商连写在一起,那么就要用一个中间变量分两步走,先求余,再求商,分开来操作。比如:
  1. 8=8562%10000/1000;
复制代码

分成两步走:
  1. a=8562%10000;
  2. a=a/1000;
复制代码

上述的变量a就是引入的中间变量。
现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;
  6.   unsigned char b;
  7.   unsigned char c;
  8.   unsigned char d;
  9.         unsigned int  x=8562;  //初始化为8562,注意必须是int类型以上,不能是char类型,char最大范围是255。

  10.   a=x%10000/1000;  //拆分提取千位
  11.   b=x%1000/100;    //拆分提取百位
  12.   c=x%100/10;      //拆分提取十位
  13.   d=x%10/1;        //拆分提取个位


  14.        
  15.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  16.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示
  17.   GuiWdData2=c;   //把c这个变量放到窗口变量2里面显示
  18.   GuiWdData3=d;   //把d这个变量放到窗口变量3里面显示

  19.         
  20. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  21.    while(1)  
  22.    {
  23.       initial();
  24.       key_service();
  25.       display_service();
  26.    }

  27. }
复制代码

    查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。上坚鸿51学习板观察程序执行的结果如下:
     变量a为8。
     变量b为5。
     变量c为6。
     变量d为2。
     下节预告:逻辑运算符的“与”运算。
(未完待续)

乐于分享,勇于质疑!
63#
 楼主| 发表于 2015-7-27 08:24:59 | 只看该作者
第二十八节:整除求余的运算。
    求余跟上一节讲的求商都是属于整除运算,区别是:求余返回余数,求商返回商。整除求余的余数有一个很明显的规律:余数永远小于除数(除数不为0的情况下)。比如,除数是10,那么不管被除数有多大,余数必然是从0到9的数,不可能是10以上的数。上一节提到除法求商的运算符号是“/”,而除法求余的运算符号是“%”,外形跟百分号一致,只是在C语言中用来做除法求余的运算符而已。
整除求余的通用格式:
“保存变量”=“被除数”% “除数1% “除数2...%  “除数N;
跟之前讲的加减运算一样,左边的“保存变量”必须是变量,右边的可以是变量和常量的任意组合。如果右边只有两个参与运算的数据,就是整除求余的常见格式。
整除求余的常见格式:
“保存变量”=“被除数” % “除数” ;       
现在从整除求余常见格式的6个方面来分析它的规律。
(1)当“除数”等于0时。我们都知道,数学运算除数是不允许等于0的,如果在单片机中非要让除数为0,余数会出现什么结果?我在keilC51编译环境试过,发现有一个规律:如果“除数”是变量的0,那么余数等于被除数。如果“除数”是常量的0,那么余数等于1。还有一种特殊的情况是编译不通过的,就是“被除数”是变量,而“除数”是常量的0。其实大家都知道“除数”不能为0,为什么我非要做“除数”为0时的实验呢?意义何在?我虽然知道除数为0时会出错,但是我不知道这个错到底严不严重,会不会程序崩溃,当我做了这个实验后,我心中的石头才放下了,万一除数为0,只是运算出错,至少程序不会崩溃,这样我心里就有了一个底,当哪天我某个程序崩溃时,我至少可以排除了这种情况,方便我找bug。这就是本实验的意义所在。
    比如:
  1. a=23%y;  //假设除数变量y里面是0,那么a的结果是23。
  2. b=23%0;  //除数是常量0,那么b的结果是1。
  3. b=g%0;  //这种特殊情况编译不通过:“被除数”是变量,而“除数”是常量的0。
复制代码

(2)当被除数小于“除数”时。余数等于被除数本身。比如:
  1. c=7%10;  //c的结果是7。
复制代码

(3)当被除数等于“除数”时。余数等于0。比如:
  1. d=10%10;  //d的结果是0。
复制代码

(4)当被除数大于“除数”时。余数也必然小于“除数”。
比如:
  1. e=10%4;  //e的结果是2。
  2. f=10% 3;  //f的结果是1。
复制代码

(5)自除求余运算的简写。跟前面加减法一样,当“被除数”是“保存变量”时,存在自除求余运算的简写。
“保存变量”=“保存变量” % “除数” ;
上述自除运算的简写如下:
“保存变量” % =“除数” ;
比如:
  1. g%=5;  //相当于g=g%5;
复制代码

加减法有自加1++g”和自减1g--”的特殊写法,但是除法不存在这种自除1的特殊写法,因为一个数除以1还是等于它本身,所以自除1没有任何意义,因此C语言语法中没有这种写法。
   (6)除法的溢出规律跟加法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了避免一不小心就溢出的问题,我强烈建议,不管加减乘除,凡是参与运算的变量全部都要转化成unsigned long变量,转化的方法也跟加减运算的转换方法一致,不再详细讲解这方面的内容。
现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;
  6.   unsigned char b;
  7.   unsigned char c;
  8.   unsigned char d;
  9.   unsigned char e;
  10.   unsigned char f;
  11.         unsigned char g=10;  //初始化为10
  12.         unsigned char y=0; //除数变量初始化为0。
  13.         a=23%y;
  14.         b=23%0;
  15.         //b=g%0;  //这种特殊情况编译不通过:“被除数”是变量,而“除数”是常量的0。
  16.         c=7%10;
  17.         d=10%10;
  18.         e=10%4;
  19.         f=10%3;
  20.         g%=5;  //相当于g=g%5;
  21.        
  22.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  23.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示
  24.   GuiWdData2=c;   //把c这个变量放到窗口变量2里面显示
  25.   GuiWdData3=d;   //把d这个变量放到窗口变量3里面显示
  26.   GuiWdData4=e;   //把e这个变量放到窗口变量4里面显示
  27.   GuiWdData5=f;   //把f这个变量放到窗口变量5里面显示
  28.   GuiWdData6=g;   //把g这个变量放到窗口变量5里面显示
  29.         
  30. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  31.    while(1)  
  32.    {
  33.                   initial();
  34.       key_service();
  35.       display_service();
  36.    }

  37. }
复制代码

    查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。上坚鸿51学习板观察程序执行的结果如下:
     变量a为23。
     变量b为1。
     变量c为7。
     变量d为0。
    变量e为2。
    变量f为1。
    变量g为0。
     下节预告:利用“整除求商求余”来提取一个数的个十百千位。
(未完待续)

乐于分享,勇于质疑!
62#
 楼主| 发表于 2015-7-18 07:01:13 | 只看该作者
第二十七节:整除求商的运算。
C语言中,乘法符号不是“×”而是“*”,除法求商的符号不是“÷”而是“/”。乘除法符号跟我们日常用的数学符号有点不一样,我个人猜测C语言这样规定的原因是因为“×”容易跟大写字母“X”搞混,而“÷”这个符号在电脑键盘上不方便直接输入,故分别用“*”和“/”替代。
何谓“整除”?请看以下两个对比例子:
10除以4,商等于2.5------(带小数点)
10除以4,商等于2,余数是2------(这就叫做整除)
什么时候带小数点,什么时候是整除?取决于参与运算的变量类型。标准的C语言中,其实远远不止我前面所说的unsigned char ,unsigned int ,unsigned long这三种类型,还有一种叫浮点数的float类型,当参与运算的变量涉及float类型时,就可能存在小数点。这是题外话,大家大概知道有这么一回事即可,暂时不用深入研究float等其它类型的数据,因为在单片机项目中,只要用我所述的三种常用类型就绝对够用了,单片机不用涉及float类型,如果项目涉及小数点的显示和处理,我们完全可以用那三种类型去处理它,这些处理方法我后续会讲到,暂时不用管。而unsigned char ,unsigned int ,unsigned long这三种类型的除法都是整除,我后续所讲的所有章节内容也都是整除。
整除的通用格式:
“保存变量”=“被除数” /  “除数1/  “除数2... /  “除数N;
跟之前讲的加减运算一样,左边的“保存变量”必须是变量,右边的可以是变量和常量的任意组合。如果右边只有两个参与运算的数据,就是整除的常见格式。
整除的常见格式:
“保存变量”=“被除数” /  “除数” ;
现在从整除常见格式的6个方面来分析它的规律。
(1)当“除数”等于0时。我们都知道,数学运算除数是不允许等于0的,如果在单片机中非要让除数为0,商会出现什么结果?我试过,发现有一个规律:如果“除数”是变量的0,那么商等于十进制的255(十六进制是0xff)。如果“除数”是常量的0,那么商等于十进制的1。比如:
  1. a=23 /y;  //假设除数变量y里面是0,那么a的结果是255(十六进制的0xff)。
  2. b=23 /0;  //除数是常量0,那么b的结果是1。
复制代码

(2)当被除数小于“除数”时。商等于0。比如:
  1. c=7 / 10;  //c的结果是0。
复制代码

(3)当被除数等于“除数”时。商等于1。比如:
  1. d=10/ 10;  //d的结果是1。
复制代码

(4)当被除数大于“除数”时。商大于0
比如:
  1. e=10/ 4;  //e的结果是2。
  2. f=10/ 3;  //f的结果是3。
复制代码

(5)自除运算的简写。跟前面加减法一样,当“被除数”是“保存变量”时,存在自除运算的简写。
“保存变量”=“保存变量” /  “除数” ;
上述自除运算的简写如下:
“保存变量” / =“除数” ;
比如:
  1. g/=5;  //相当于g=g/5;
复制代码

加减法有自加1++g”和自减1g--”的特殊写法,但是除法不存在这种自除1的特殊写法,因为一个数除以1还是等于它本身,所以自除1没有任何意义,因此C语言语法中没有这种写法。
   (6)除法的溢出规律跟加法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了避免一不小心就溢出的问题,我强烈建议,不管加减乘除,凡是参与运算的变量全部都要转化成unsigned long变量,转化的方法也跟加减运算的转换方法一致,不再详细讲解这方面的内容。
现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;
  6.   unsigned char b;
  7.   unsigned char c;
  8.   unsigned char d;
  9.   unsigned char e;
  10.   unsigned char f;
  11.         unsigned char g=10;  //初始化为10
  12.         unsigned char y=0; //除数变量初始化为0。
  13.         a=23/y;
  14.         b=23/0;
  15.         c=7/10;
  16.         d=10/10;
  17.         e=10/4;
  18.         f=10/3;
  19.         g/=5;  //相当于g=g/5;
  20.        
  21.   GuiWdData0=a;   //把a这个变量放到窗口变量0里面显示
  22.   GuiWdData1=b;   //把b这个变量放到窗口变量1里面显示
  23.   GuiWdData2=c;   //把c这个变量放到窗口变量2里面显示
  24.   GuiWdData3=d;   //把d这个变量放到窗口变量3里面显示
  25.   GuiWdData4=e;   //把e这个变量放到窗口变量4里面显示
  26.   GuiWdData5=f;   //把f这个变量放到窗口变量5里面显示
  27.   GuiWdData6=g;   //把g这个变量放到窗口变量5里面显示
  28.         
  29. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  30.    while(1)  
  31.    {
  32.                   initial();
  33.       key_service();
  34.       display_service();
  35.    }

  36. }
复制代码

    查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。上坚鸿51学习板观察程序执行的结果如下:
     变量a为255(十六进制是0xff)。
     变量b为1。
     变量c为0。
     变量d为1。
    变量e为2。
    变量f为3。
    变量g为2。
     下节预告:整除求余的运算。
(未完待续)

乐于分享,勇于质疑!
61#
 楼主| 发表于 2015-7-12 07:03:03 | 只看该作者
本帖最后由 jianhong_wu 于 2015-7-12 07:32 编辑

第二十六节:乘法运算的溢出。
     乘法的溢出规律跟加法的溢出规律是一样的。举一个例子如下:
  1.    unsigned char k=30;
  2.    unsigned char n=10;
  3.    unsigned char a;
  4.    a=k*n;  
复制代码


分析:
kn相乘,相当于30乘以10,运算结果是300(十六进制是0x012c保存在一个隐藏中间变量,根据前面加法运算的规律,我猜测这个隐藏中间变量可能是unsigned int类型,然后再把这个中间变量赋值给单字节变量a,a只能接收十六进制的低8位字节0x2c,所以运算后a的数值由于溢出变成了十六进制的0x2c(十进制是44)。
由于乘法的溢出规律跟加法的溢出规律是一样的,所以不再多举例子。在实际项目中,为了避免一不小心就溢出的问题,我强烈建议,不管加减乘除,凡是参与运算的变量全部都要转化成unsigned long变量,转化的方法也跟加减运算的转换方法一致,不再详细解决这方面的内容。
现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始-----------------------------------------------------------------

  4. ----------*/
  5.       
  6.    unsigned char k=30;
  7.    unsigned char n=10;
  8.    unsigned char a;

  9.    a=k*n;  

  10.    GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示



  11.         
  12. /*---C语言学习区域的结束-----------------------------------------------------------------

  13. ----------*/
  14.    while(1)  
  15.    {
  16.       initial();
  17.       key_service();
  18.       display_service();
  19.    }

  20. }
复制代码

    查看运算结果的方法。如何在坚鸿51学习板上观察变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。上坚鸿51学习板观察程序执行的结果如下:
       变量a为0x2c(十进制是44)。

     下节预告:除法运算的常见格式。
(未完待续)

乐于分享,勇于质疑!
60#
 楼主| 发表于 2015-7-6 10:00:13 | 只看该作者
本帖最后由 jianhong_wu 于 2015-7-6 10:09 编辑

第二十五节:连乘以及自乘运算的简写。
       上一节我列举的乘法例子中,右边的参与运算的数据都是两个。实际上,C语言规则没有限制数据个数,它的通用格式如下:
       “保存变量”=“被乘数1”*“乘数2”...*“乘数N”;
       当右边的乘数个数超过两个的时候,就是我所说的“连乘”,每个乘数的属性没有限定,可以是常量,也可以是变量。比如:
  1.   a=2*5*3;  //被乘数和乘数全部是常量。a的结果为30。
  2.   b=k*x*y;  //被乘数和乘数全部是变量。b的结果为36。
  3.   c=x*5*y;  //被乘数和乘数,有的是常量,有的是变量。c的结果为90。
复制代码

        连乘的运行顺序是,赋值符号“=”右边的乘数挨个相乘,把每一次的运算结果放在一个临时的隐蔽变量里,这个隐蔽的变量我们看不到,是单片机系统内部参与运算时的专用寄存器,等右边所有的乘数连乘的计算结果出来后,再把这个隐蔽变量所保存的计算结果赋值给左边的“保存变量”。
        讲完了连乘的格式,接着讲自乘的简写。何谓自乘?当右边的被乘数是“保存变量”本身时,这种情况就是我所说的“自乘”。比如:
“保存变量”=“保存变量”*“乘数1”;
“保存变量”=“保存变量”*“乘数1”*“乘数2”...*“乘数N”;
        上述自加计算式可以简写成如下格式:
“保存变量”*=“乘数1”;
“保存变量”*=“乘数1”*“乘数2”...*“乘数N”;
        这种格式就是我所说的自乘简写。现在举几个例子如下:
  1.   d*=6;     //相当于d=d*6;最后d的结果为30。
  2.   e*=x;     //相当于e=e*x;最后e的结果为15。
  3.   f*=2*y*k; //相当于f=f*(2*y*k);最后f的结果为120。
复制代码

     我之前在讲加法的自加和减法的自减运算时,还给大家介绍了它们另外一种特殊的简写方式。比如减法运算,当右边只有2减数,当一个减数是“保存变量”,另一个是常数1时,格式如下:
“保存变量”=“保存变量”-1;
        这时候,可以把上述格式简写成如下两种格式:
“保存变量”--;
--“保存变量”;
        这两种格式也是俗称的“自减1”操作。比如:
  1. g--;  //相当于g=g-1或者g-=1;
  2. --h;  //相当于h=h-1或者h-=1;
复制代码

      那么,本节所讲的自乘运算,有没有这种特殊写法“g**”或者“**h”?答案很明显,没有。因为任何一个数“自乘1”还是等于它本身,所以研究这种特殊写法就没有任何意义。
      现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:



  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  6.   unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  7.   unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  8.   unsigned char d=5;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为5.
  9.   unsigned char e=5;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为5.
  10.   unsigned char f=5;       //定义一个变量f,并且分配了1个字节的RAM空间。初始化默认为5.
  11.    
  12.   unsigned char x=3;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为3.
  13.   unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        
  14.   unsigned char k=2;     //定义一个变量k,并且分配了1个字节的RAM空间。初始化默认为2.
  15.         
  16.         
  17.             //第1个知识点:连乘。
  18.   a=2*5*3;  //被乘数和乘数全部是常量。a的结果为30。
  19.   b=k*x*y;  //被乘数和乘数全部是变量。b的结果为36。
  20.   c=x*5*y;  //被乘数和乘数,有的是常量,有的是变量。c的结果为90。

  21.             //第2个知识点:自乘的简写。
  22.   d*=6;     //相当于d=d*6;最后d的结果为30。
  23.   e*=x;     //相当于e=e*x;最后e的结果为15。
  24.   f*=2*y*k; //相当于f=f*(2*y*k);最后f的结果为120。
  25.          
  26.          


  27.    GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  28.    GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  29.    GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  30.    GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  31.    GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示
  32.    GuiWdData5=f;   //把变量f这个数值放到窗口变量5里面显示


  33.         
  34. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  35.    while(1)  
  36.    {
  37.       initial();
  38.       key_service();
  39.       display_service();
  40.    }

  41. }
复制代码

      如何在坚鸿51学习板上观察a,b,c,d,e,f这6个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:
变量a为30
变量b为36。
变量c为90。
变量d为30。
变量e为15。
变量f为120。
     下节预告:乘法运算的溢出。
(未完待续)



乐于分享,勇于质疑!
59#
 楼主| 发表于 2015-6-28 10:21:11 | 只看该作者
第二十四节:乘法运算的5种常见格式。
      请先看以下的乘法语法格式:
       “保存变量”=“被乘数1”*“乘数2”*... *“乘数N”;
        含义是:右边的“被乘数”与各“乘数”相乘,并且把最终的运算结果赋值给左边的“保存变量”。注意,这里的符号“=”不是等于号的意思,而是赋值的意思。左边的“保存变量”必须是变量,不能是常量,否则编译时会报错。右边的“被乘数”和“乘数”既可以是变量,也可以是常量,也可以是“保存变量”本身自己。多说一句,何谓变量和常量?变量是可以在程序中被更改的,是被分配的一个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。
       现在编写一个程序来练习上述5种格式的乘法语句,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  6.   unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  7.   unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  8.   unsigned char d=18;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为18.
  9.   unsigned char e=2;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为2.
  10.         
  11.   unsigned char x=15;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为15.
  12.   unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        

  13.   //第1种:“被乘数1”是常量,“乘数2”是常量。
  14.   a=15*3;
  15.         
  16.         
  17.   //第2种:“被乘数1”是变量,“乘数2”是常量。
  18.   b=x*10;
  19.         
  20.   //第3种:“被乘数1”是变量,“乘数2”是变量。
  21.   c=x*y;
  22.         
  23.         
  24.   //第4种:“被乘数1”是保存变量本身,“乘数2”是常量。
  25.   d=d*2;
  26.   d=d*7;
  27.         
  28.         
  29.   //第5种:“被乘数1”是保存变量本身,“乘数2”是变量。
  30.   e=e*x;
  31.   e=e*y;


  32.   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  33.   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  34.   GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  35.   GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  36.   GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示

  37.         
  38. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  39.    while(1)  
  40.    {
  41.       initial();
  42.       key_service();
  43.       display_service();
  44.    }

  45. }
复制代码

        如何在坚鸿51学习板上观察a,b,c,d,e这5个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:
变量a为45。
变量b为150。
变量c为90。
变量d为252。
变量e为180。
     下节预告:乘法连写的简写。
(未完待续)

乐于分享,勇于质疑!
58#
 楼主| 发表于 2015-6-21 08:24:41 | 只看该作者
本帖最后由 jianhong_wu 于 2015-6-21 08:26 编辑

第二十三节:建议把所有参与减法运算的变量都转换成unsigned long数据类型。
    不管是以前讲的加法,现在讲的减法,还是未来讲的乘法和除法,我都会强烈建议“请把所有参与运算的变量都转成unsigned long类型”。unsigned long变量是三种数据类型中取值范围最大的数,取值范围可达04294967295之间,用了此类型变量的运算,不会轻易导致运算溢出的问题。有细心读者会问,万一数据超过了4294967295怎么办?答:可用BCD码的数组方式进行运算。这种数组运算的方法我在《从业将近十年,手把手教你单片机程序框架》里用了好几个章节跟大家介绍过,初学者暂时不用深入学习它。
变量转换的方法是引入中间变量,有多少个需要转换的变量就引入多少个中间变量,请看下面这个例子。
转换之前:
  1. unsigned int  a;
  2. unsigned char x=195;
  3. unsigned long y=101;
  4. a=x-y;
复制代码

分析:上述公式用到3个变量,其中ax都不是unsigned long变量,因此需要为它们分别引入中间变量ts
转换之后:
  1. unsigned int  a;
  2. unsigned char x=195;
  3. unsigned long y=101;

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

  6. s=0;  //s在接收x原数据之前先把高位和低位全部清零。因为s和x的数据宽度不一。
  7. s=x;  //接收x原数据,相当于把x转换成unsigned long中间变量。

  8. t=s-y;  //此处的t就默认代表了变量a。
复制代码

本章虽短,但是此方法在实际项目中很重要,大家不可大意。
     下节预告:乘法运算的5种常见格式。
(未完待续)
乐于分享,勇于质疑!
57#
 楼主| 发表于 2015-6-17 21:27:07 | 只看该作者
本帖最后由 jianhong_wu 于 2015-6-17 21:33 编辑

第二十二节:减法运算的溢出。
       在开始本章节之前,先纠正一下前面第17节内容的一个小bug。我原文中写道:
       “保存变量”+=“加数1”+“加数2”+...+“加数N”;
相当于:
       “保存变量”=“保存变量”+“加数1”+“加数2”+...+“加数N”;
        当时我没有考虑到优先级,漏了一个括号,修改后,
相当于:
       “保存变量”=“保存变量”+(“加数1”+“加数2”+...+“加数);
         这样才算比较准确。同理,我后面所举的例子:
         f+=18+y+k; //相当于f=f+18+y+k;
在注释中也漏了一个括号,应该是:
         f+=18+y+k; //相当于f=f+(18+y+k);
         上述多一个括号或者少一个括号虽然看似不影响运算结果,但是运算顺序是有点不一样的。


         现在正式开始讲本节减法溢出的问题。英文“unsigned”的中文意思就是”无符号的”,延伸含义是“无负号无负数”的意思,所以unsigned char ,unsigned int ,unsigned long这三种类型数据都是无负号无负数的,取值只能是0和正数,那么问题来了,当被减数小于减数的时候,运算结果会是什么样子,有什么规律?
(1)第一个例子:
  1. unsigned char a;
  2. a=0-1;
复制代码

分析:
左边的“保存变量”a的数据长度是1个字节8位,a=0-1可以看成是十六进制的a=0x00-0x01。由于0x000x01小,所以假想一下需要向高位借位,借位后成了a=0x100-0x01。所以a的最终结果是0xff(十进制是255)。根据”假想借位”这个规律,如果是b也是unsigned char 类型,那么b=2-5自然就相当于b=0x102-0x05,运算结果b等于0xfd(十进制是253)
(2)第二个例子:
  1. unsigned int c;
  2. c=0-1;
复制代码

分析:
左边的“保存变量”c的数据长度是2个字节16位,c=0-1可以看成是十六进制的c=0x0000-0x0001。由于0x00000x0001小,所以假想一下需要向高位借位,借位后成了c=0x10000-0x0001。所以c的最终结果是0xffff(十进制是65535)。根据”假想借位”这个规律,如果是d也是unsigned  int 类型,那么d=2-5自然就相当于b=0x10002-0x0005,运算结果b等于0xfffd(十进制是65533)
          为了验证上述抛出的”假想借位”,现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:


  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         

  5.         
  6.         
  7.   unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  8.   unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  9.   unsigned int c;        //定义一个变量c,并且分配了2个字节的RAM空间。
  10.   unsigned int d;        //定义一个变量d,并且分配了2个字节的RAM空间。

  11.    //第一个例子,针对a与b都是unsigned char类型数据。     
  12.    a=0-1;  
  13.    b=2-5;
  14.         
  15.         
  16.         //第二个例子,针对c与d都是unsigned int类型的数据。
  17.    c=0-1;
  18.    d=2-5;        


  19.    GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  20.    GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  21.    GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  22.    GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  23.         
  24. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  25.    while(1)  
  26.    {
  27.                   initial();
  28.       key_service();
  29.       display_service();
  30.    }

  31. }
复制代码


          查看运算结果的方法。如何在坚鸿51学习板上观察a,b,c,d这4个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。按下S9按键不松手就可以切换到十六进制的显示界面,松开手后会自动切换到十进制的界面。上坚鸿51学习板观察程序执行的结果如下:

          变量a为0xff(十进制是255)
          变量b为0xfd(十进制是253)
          变量c为0xffff(十进制是65535)
          变量d为0xfffd(十进制是65533)

          下节预告:建议减法运算前先把所有变量转换成同一数据类型再参与运算。
(未完待续)

乐于分享,勇于质疑!
56#
 楼主| 发表于 2015-6-8 07:48:02 | 只看该作者
第二十一节:减法的连写和自减运算的简写。
     连减。上一节我列举的减法例子中,右边的减数只有一个。实际上,C语言规则没有限制减数的个数,它的通用格式如下:
      “保存变量”=“被减数”-“减数1”-“减数2”-...-“减数N”;
      被减数与减数的属性。当右边的减数个数总共超过1个的时候,就是我所说的“连减”。被减数和减数的属性没有限定,可以是常量,也可以是变量。比如:
     a=68-3-15;     //被减数和减数全部是常量。
     b=q-x-y-k;    //被减数和减数全部是变量。
     c=63-x-5-k;   //被减数和减数,有的是常量,有的是变量。
       连减的运行顺序。赋值符号“=”右边的被减数挨个与减数相减,每一次的运算结果都放在一个临时的隐蔽变量里,这个隐蔽的变量我们看不到,是单片机系统内部参与运算时的专用寄存器,当与所有减数相减的计算结果出来后,再把隐蔽变量所保存的计算结果赋值给左边的“保存变量”。
       自减。当被减数是“保存变量”本身时,这种情况就是我所说的“自减”。比如:
“保存变量”=“保存变量”-“减数1”;
“保存变量”=“保存变量”-“减数1”-“减数2”-...-“减数N”;
       自减的简写。当被减数是“保存变量”本身,并且只有一个减数时,那么上述自减计算式可以简写成如下格式:
     “保存变量”-=“减数1”;
     “保存变量”-=“减数1”-“减数2”-...-“减数N”;
       这种格式就是我所说的自减简写。现在举几个例子如下:
     d-=6;  //相当于d=d-6;
     e-=x;  //相当于e=e-x;
     f-=18-y-k; //相当于f=f-(18-y-k);
       自减的特殊简写。在自减运算中,只有一个减数,并且这个减数是常数1时,格式如下:
     “保存变量”=“保存变量”-1;
       这时候,可以把上述格式简写成如下两种格式:
     “保存变量”--;
     --“保存变量”;
       这两种格式也是俗称的“自减1”操作。比如:
      g--;  //相当于g=g-1或者g-=1;
      --h;  //相当于h=h-1或者h-=1;
        自减1符号“--”可以在变量的左边,也可以在变量的右边,它们在这里本质是一样的,没有差别。当然,如果是在循环条件语句中,这时自减1符号“--”在左边还是在右边是有一点点微弱的差别,这方面的内容以后再讲。
        上机练习。现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  6.   unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  7.   unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  8.   unsigned char d=65;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为65.
  9.   unsigned char e=38;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为38.
  10.   unsigned char f=29;       //定义一个变量f,并且分配了1个字节的RAM空间。初始化默认为29.
  11.   unsigned char g=5;       //定义一个变量g,并且分配了1个字节的RAM空间。初始化默认为5.        
  12.   unsigned char h=5;       //定义一个变量h,并且分配了1个字节的RAM空间。初始化默认为5.

  13.         
  14.   unsigned char q=50;    //定义一个变量q,并且分配了1个字节的RAM空间。初始化默认为50.
  15.   unsigned char x=3;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为3.
  16.   unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        
  17.   unsigned char k=2;     //定义一个变量k,并且分配了1个字节的RAM空间。初始化默认为2.

  18.   //第1个知识点:连减。
  19.   a=68-3-15;//被减数和减数全部是常量。a的结果为:50。
  20.   b=q-x-y-k;//被减数和减数全部是变量。b的结果为:39。
  21.   c=63-x-5-k;//被减数和减数,有的是常量,有的是变量。c的结果为:53。
  22.        
  23.        
  24.   //第2个知识点:自减的常规格式。   
  25.   d-=6;//相当于d=d-6;  d的结果为:59。
  26.   e-=x;//相当于e=e-x;  e的结果为:35。
  27.   f-=18-y-k;//相当于f=f-(18-y-k);  f的结果为:19。
  28.        
  29.        
  30.   //第3个知识点:自减的特殊格式。
  31.   g--;//相当于g=g-1或者g-=1;  g的结果为:4。
  32.   --h;//相当于h=h-1或者h-=1;  d的结果为:4。
  33.          

  34.    GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  35.    GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  36.    GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  37.    GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  38.    GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示
  39.    GuiWdData5=f;   //把变量f这个数值放到窗口变量5里面显示
  40.    GuiWdData6=g;   //把变量g这个数值放到窗口变量6里面显示
  41.    GuiWdData7=h;   //把变量h这个数值放到窗口变量7里面显示



  42.         
  43. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  44.    while(1)  
  45.    {
  46.       initial();
  47.       key_service();
  48.       display_service();
  49.    }

  50. }
复制代码

      查看运算结果的方法。如何在坚鸿51学习板上观察a,b,c,d,e,f,g,h这8个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:
      变量a为50。
      变量b为39。
      变量c为53。
      变量d为59。
      变量e为35。
      变量f为19。
      变量g为4。
      变量h为4。
       下节预告:减法运算的溢出。
(未完待续)

乐于分享,勇于质疑!
55#
 楼主| 发表于 2015-5-31 07:44:38 | 只看该作者
第二十节:减法运算的5种常见格式。
      请先看以下的减法语法格式:
       “保存变量”=“减数1”-“减数2”-...-“减数N”;
        含义是:右边的“减数”与“减数”相减,并且把最终的运算结果赋值给左边的“保存变量”。注意,这里的符号“=”不是等于号的意思,而是赋值的意思。左边的“保存变量”必须是变量,不能是常量,否则编译时会报错。右边的“减数”既可以是变量,也可以是常量,也可以是“保存变量”本身自己。多说一句,何谓变量和常量?变量是可以在程序中被更改的,是被分配的一个RAM空间。常量往往是数字,或者是被分配在ROM空间的一个具体数值。下面根据右边“被减数”与“减数”的不同组合,列出了减法运算的5种常见格式。
      第1种:“减数1”是常量,“减数2”是常量。比如:
unsigned char a;
a=15-3;
数字“15”和“3”都是常量。执行上述语句后,保存变量a变成了12。
      第2种:“减数1”是变量,“减数2”是常量。比如:
unsigned char b;
unsigned char x=15;
b=x-10;
x是变量,“10”是常量。由于原来x变量里面的数值是15,执行上述语句后,保存变量b变成了5。而变量x则保持不变,x还是15。
      第3种:“减数1”是变量,“减数2”是变量。比如:
unsigned char c;
unsigned char x=15;
unsigned char y=6;
c=x-y;
x是变量,y也是变量。由于原来x变量里面的数值是15,y变量里面的数值是6,执行上述语句后,保存变量c变成了9。而变量x和y则保持不变,x还是15,y还是6。
      第4种:“减数1”是保存变量本身,“减数2”是常量。比如:
unsigned char d=18;
d=d-2;
d=d-7;
d是保存变量,“2”和“7”都是常量。这类语句有一个特点,具备了自减功能,可以更改自己本身自己的数值。比如原来保存变量d的数值是18,执行“d=d-2;”语句后,d变成了16,接着再执行完“d=d-7;”语句后,d最后变成了9。
      第5种:“减数1”是保存变量本身,“减数2”是变量。比如:
unsigned char e=28;
unsigned char x=15;
unsigned char y=6;
e=e-x;
e=e-y;
e是保存变量,x与y都是变量。这类语句有一个特点,具备了自减功能,可以更改自己本身自己的数值。比如原来保存变量e的数值是28,执行“e=e-x;”语句后,e变成了13,接着再执行完“e=e-y;”语句后,e最后变成了7。
       现在编写一个程序来练习上述5种格式的减法语句,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  6.   unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  7.   unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  8.   unsigned char d=18;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为18.
  9.   unsigned char e=28;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为28.
  10.         
  11.   unsigned char x=15;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为15.
  12.   unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        

  13.   //第1种:“减数1”是常量,“减数2”是常量。
  14.   a=15-3;
  15.         
  16.         
  17.   //第2种:“减数1”是变量,“减数2”是常量。
  18.   b=x-10;
  19.         
  20.         
  21.   //第3种:“减数1”是变量,“减数2”是变量。
  22.   c=x-y;
  23.         
  24.         
  25.   //第4种:“减数1”是保存变量本身,“减数2”是常量。
  26.   d=d-2;
  27.   d=d-7;
  28.         
  29.         
  30.   //第5种:“减数1”是保存变量本身,“减数2”是变量。
  31.   e=e-x;
  32.   e=e-y;


  33.   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  34.   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  35.   GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  36.   GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  37.   GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示

  38.         
  39. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  40.    while(1)  
  41.    {
  42.       initial();
  43.       key_service();
  44.       display_service();
  45.    }

  46. }
复制代码

        如何在坚鸿51学习板上观察a,b,c,d,e这5个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:
变量a为12。
变量b为5。
变量c为9。
变量d为9。
变量e为7。
     下节预告:减法的连写和自减运算的简写。
(未完待续)

乐于分享,勇于质疑!
54#
发表于 2015-5-26 20:57:29 | 只看该作者
顶一下。。。。
乐于分享,勇于质疑!
回复

使用道具 举报

53#
发表于 2015-5-23 19:20:22 | 只看该作者
                  
乐于分享,勇于质疑!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|独闷闷网 ( 粤ICP备12007667号-2 )

GMT+8, 2024-5-17 15:56 , Processed in 0.239830 second(s), 14 queries .

快速回复 返回顶部 返回列表