独闷闷网

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

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

[复制链接]
21#
 楼主| 发表于 2015-4-16 19:49:35 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:24 编辑

第十三节:二进制与字节单位,以及各种定义变量的取值范围。
      为什么是二进制?人类日常生活明明是十进制的,为何数字电子领域偏要选择二进制?这是由数字硬件电路决定的。人有十个手指头,人可以发出十种不同声音来命名0,1,2,3...9这些数字,人可以肉眼识别十种不同状态的信息,但是数字电路要直接处理十进制却很难,相对来说,二进制就轻松多了。一颗LED灯的亮与灭,一根IO口的输出是高电平和低电平,读取某一个点的电压是高于2V还是低于0.8V,只需要用三极管等元器件就可把处理电路搭建起来,二进制广泛应用在数字电路的存储,通讯和运算等领域,想学好单片机就必须掌握它。
     二进制如何表示成千上万的数值?现在用LED灯的亮和灭来跟大家讲解。
  (11LED灯:
灭   第0种状态
亮   第1种状态
合计:共2种状态。
  (22LED灯挨着:
灭灭   第0种状态
灭亮   第1种状态
亮灭   第2种状态
亮亮   第3种状态
合计:共4种状态。
  (33LED灯挨着:
灭灭灭   第0种状态
灭灭亮   第1种状态
灭亮灭   第2种状态
灭亮亮   第3种状态
亮灭灭   第4种状态
亮灭亮   第5种状态
亮亮灭   第6种状态
亮亮亮   第7种状态
合计:共8种状态。
  (48LED灯挨着:
灭灭灭灭灭灭灭灭   第0种状态
灭灭灭灭灭灭灭亮   第1种状态
......
亮亮亮亮亮亮亮灭   第254种状态
亮亮亮亮亮亮亮亮   第255种状态
合计:共256种状态。
  (516LED灯挨着:
灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭   第0种状态
灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭亮   第1种状态
......
亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮灭   第65534种状态
亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮   第65535种状态
合计:共65536种状态。
  (632LED灯挨着:
灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭   
0种状态
灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭灭亮   
1种状态
......
亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮灭   
4294967294种状态
亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮亮   
4294967295种状态
合计:共4294967296种状态。

       什么是位?以上一个LED灯就代表一位,8LED灯就代表8位。一个变量的位数越大就意味着这个变量的取值范围越大。一个单片机的位数越多大,就说明这个单片机一次处理的数据范围就越大,意味着运算和处理速度就越快。我们日常所说的8位单片机,32位单片机,就是这个位的概念。为什么32位的单片机比8位单片机的处理和运算能力强,就是这个原因。位的英文名是用bit来表示。
什么是字节?字节是计算机很重要的一个基本单位,一个字节有8位。8LED灯挨着能代表多少种状态,就意味着一个字节的数据范围有多大。从上面举的例子中,我们知道8LED灯挨着,能表示从0255种状态,所以一个字节的取值范围就是从0255
       各种定义变量的取值范围。前面第十一节讲了常用变量的定义有3种,unsigned charunsigned int ,unsigned long。但是没有讲到它们的取值范围,现在讲到二进制和字节了,可以回过头来跟大家讲讲这3种变量的取值范围,而且很重要。
unsigned char的变量占用1个字节RAM,共8位,根据前面LED灯的例子,取值范围是从0255
Unsigned int的变量占用2个字节RAM,共16位,根据前面LED灯的例子,取值范围是从065535
Unsigned long的变量占用4个字节RAM,共32位,根据前面LED灯的例子,取值范围是从04294967295
       现在我们编写一个程序来验证unsigned charunsigned int的取值范围。定义两个unsigned char变量aba赋值255b赋值256255256恰好处于unsigned char的取值边界。另外再定义两个unsigned int变量cdc赋值65535d赋值655366553565536恰好处于unsigned int的取值边界。最后把程序编译后下载到坚鸿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 int c;    //定义一个变量c,并且分配了2个字节的RAM空间。
  8.   unsigned int d;    //定义一个变量d,并且分配了2个字节的RAM空间。

  9.         a=255;//把255赋值给变量a,a此时会是什么数?会超范围溢出吗?
  10.         b=256;//把256赋值给变量b,b此时会是什么数?会超范围溢出吗?
  11.         c=65535;//把65535赋值给变量c,c此时会是什么数?会超范围溢出吗?
  12.         d=65536;//把65536赋值给变量d,d此时会是什么数?会超范围溢出吗?
  13.         
  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学习板观察程序执行的结果如下:
    unsigned char变量a的数值是255。
     unsigned char变量b的数值是0。
    unsigned int  变量c的数值是65535。
     unsigned int  变量d的数值是0。
     通过以上现象分析,我们知道unsigned char变量最大能取值到255,如果非要赋值256就会超出范围溢出后变成了0。而unsigned int变量最大能取值到65535,如果非要赋值65536就会超出范围溢出后变成了0。
    多说一句,至于unsigned long的取值范围,大家暂时不用尝试,因为我现在给大家用的模板程序能观察的最大变量是16位的unsigned int类型,暂时不支持32位的unsigned long类型。
       下节预告:二进制与十六进制。
(未完待续)



乐于分享,勇于质疑!
22#
 楼主| 发表于 2015-4-19 01:39:34 | 显示全部楼层
大耳怪 发表于 2015-4-18 13:49
鸿哥基础篇,写程序模块化教学可以吗。谢谢

暂时不会讲这些。以后再看情况。
乐于分享,勇于质疑!
23#
 楼主| 发表于 2015-4-19 11:28:12 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:26 编辑

第十四节:二进制与十六进制。
       C51编译器并不支持二进制的书写格式,即使添加某个头文件后能支持二进制的书写格式,二进制的书写还是有个弊端,就是数字太多太长了,写起来非常费劲不方便,怎么办?解决办法就是用十六进制。十六进制是二进制的缩写,之所以称它为二进制的缩写,是因为它们的转换关系非常简单直观,不需要借助计算器即可相互转换。
      何谓十六进制?欲搞清楚这个问题,还得先从十进制说起。所谓十进制,就是用一位字符可以表示从09这十个数字。所谓二进制,就是用一位字符可以表示从01这二个数字。所谓十六进制,当然也就是用一位字符可以表示从015这十六个数字。但是马上就会面临一个问题,十六进制的10156个数其实是有两位字符组成的,并不是一位呀?于是C语言用一个字符A,B,C,D,E,F分别替代10,11,12,13,14,156个数,10前面的09还是跟十进制的字符一致。A,B,C,D,E,F也可以用小写a,b,c,d,e,f来替代,不区分大小写。
     前面提到了十六进制是二进制的缩写,它们的转换关系非常简单直观,每1位十六进制的字符,对应4位二进制的字符。关系如下:
十进制       二进制      十六进制
0               0000        0
1               0001        1
2               0010        2
3               0011        3
4               0100        4
5               0101        5
6               0110        6
7               0111        7
8              1000        8
9              1001        9
10            1010        A
11            1011        B
12            1100        C
13            1101        D
14            1110        E
15            1111        F

        二进制转换成十六进制的时候,如果不是4位的倍数,则最左边高位默认补上0凑合成4位的倍数。比如二进制101001,可以在左边补上20变成00101001,然后把每4位字符转成1个十六进制的字符。左边高40010对应十六进制的2,右边低41001对应十六进制的9,所以合起来最终的十六进制是29
        十六进制的标准书写格式。刚才提到的十六进制29,在C语言里不能直接写29,否则就跟十进制的写法混淆了。为了把十六进制和十进制的书写格式进行区分,C语言规定凡是十六进制必须加一个数字0和一个字母x作为前缀,也就是十六进制必须以0x作为前缀,刚才的十六进制29就应该写成0x29。凡是不加前缀的就默认为十进制。
       现在我们编写一个程序来观察十六进制和二进制的关系,最后把程序编译后下载到坚鸿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;    //定义一个变量d,并且分配了1个字节的RAM空间。

  9.   a=0x06;   //十六进制前记得加0x前缀,超过9部分的字母不分大小写。
  10.   b=0x0A;   //十六进制前记得加0x前缀,超过9部分的字母不分大小写。
  11.   c=0x0e;   //十六进制前记得加0x前缀,超过9部分的字母不分大小写。
  12.   d=0x2C;   //十六进制前记得加0x前缀,超过9部分的字母不分大小写。
  13.         
  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学习板上观察十六进制和二进制?S1S5按键是切换窗口按键。按住S9按键不松手,就可以观察当前窗口数据的十六进制格式了。松开S9按键就是当前窗口的十进制数据格式。而坚鸿51学习板右上角的16LED灯就代表了当前窗口的二进制,亮的代表1,灭的代表0
       上坚鸿51学习板观察程序执行的结果如下:
                                   十六进制         二进制         十进制     
unsigned char变量a      6                   0000 0110       6
unsigned char变量b      A                   0000 1010       10
unsigned char变量c      E                   0000 1110       14
unsigned char变量d      2C                 0010 1100       44

        多说一句,在程序里,可以用十六进制,也可以用十进制,比如:
d=0x2Cd=44的含义是一样的。十六进制的0x2C和十进制的44最终都会被C51编译器翻译成二进制00101100
       下节预告:十进制与十六进制。
(未完待续)





乐于分享,勇于质疑!
24#
 楼主| 发表于 2015-4-23 23:10:41 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:27 编辑

第十五节:十进制与十六进制。
       十六进制是二进制的缩写形式,而C语言程序里只用了十进制和十六进制这两种书写格式。它们各有什么应用特点?十六进制方便人理解机器,通常应用在配置寄存器,底层通讯驱动,底层IO口驱动,以及数据的移位,转换和合并等场合。而十进制方便人理解值的大小,在应用层经常用总之,进制数据的表现形式而已。
    十进制与十六进制如何相互转换?其实很多教科书上有介绍它们之间如何通过手工计算进行转换的方法。但是实际应用中,我从来没有用过这种手工计算方法,我用的方法是最简单直接的,就是借助电脑自带的计算器进行转换即可。现在把这种方法介绍给大家。
    第一步:点击电脑左下角“开始”菜单,在下拉菜单中把鼠标移动到“所有程序”,在下拉菜单中把鼠标移动到“附件”,在下拉菜单中点击“计算器”,此时会弹出“计算器” 的窗口。
    第二步:点击计算器窗口上面的“查看”菜单,在下拉菜单中点击“科学型”,此时“计算器” 的窗口会变长。按键上方出现“十六进制”,“十进制”,“八进制”,“二进制”等单选项。
    第三步:在按键上方“十六进制”,“十进制”,“八进制”,“二进制”等单选项中,单击所要切换到的进制,然后按数字按键输入数据。输完数据后,再单击切换到所要转换的进制中,即可完成各进制的数据切换。注意,在切换到“十六进制”的时候,在右边“四字”,“双字”,“单字”,“字节”中选中“四字”。
    第四步:把十进制转换到十六进制的方法如下:单击切换到“十进制”,然后按数字按键输入数据。输完数据后,再单击切换到“十六进制”,即可完成进制的转换。比如输入十进制的“230”,切换到十六进制就变成了“E6”。
    第五步:把十六进制转换到十进制的方法如下:单击切换到“十六进制”,然后按数字按键输入数据。输完数据后,再单击切换到“十进制”,即可完成进制的转换。比如输入十六进制的“AC”,切换到十进制就变成了“172”。
现在我们编写一个程序来观察十进制和十六进制的关系,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a;    //定义一个变量a,并且分配了1个字节的RAM空间。
  6.   unsigned char b;    //定义一个变量b,并且分配了1个字节的RAM空间。

  7.   a=230;    //把十进制的230赋值给变量a,在坚鸿51学习板上观察一下它的十六进制是不是E6。
  8.   b=0xAC;   //把十六进制的AC赋值给变量b,在坚鸿51学习板上观察一下它的十进制是不是172。
  9.          
  10.   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  11.   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示

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

  20. }
复制代码


如何在坚鸿51学习板上观察十进制和十六进制?S1S5按键是切换窗口按键。按住S9按键不松手,就可以观察当前窗口数据的十六进制格式了。松开S9按键就是当前窗口的十进制数据格式。而坚鸿51学习板右上角的16LED灯就代表了当前窗口的二进制,亮的代表1,灭的代表0
上坚鸿51学习板观察程序执行的结果如下:
                      十六进制        十进制     
unsigned char变量a      E6            230
unsigned char变量b      AC            172

     下节预告:判断语句“if”和等于关系符“==”。
(未完待续)





乐于分享,勇于质疑!
25#
 楼主| 发表于 2015-5-2 10:42:46 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:28 编辑

第十六节:加法运算的5种常用格式。
       根据上一节的预告,本来这节应该讲判断语句的,但是考虑到后续章节的连贯性,决定先讲运算语法。
       在讲运算语法之前,先讲一个我在前面忘了讲的知识点,那就是注释语句。何谓注释语句?在我前面一些章节的main函数中,经观察,发现一个规律,凡是中文解说的文字,要么前面有符号”//”,要么就是被包含在“/*”和”*/”之间。符号“//”和“/*  */”都是注释语句。注释语句是用来添加文字备忘,方便程序员阅读记忆的。在注释语句里的文字是不会被编译器翻译成机器码的,也就是说即使注释里面的文字再多,也不会增加单片机的程序容量,它是被编译器过滤忽略的,仅仅方便程序员做备注文字而已。
符号“//”和“/*  */”都是注释语句,但应用方面有点小差异。符号“//”是用来注释一行文字。而“/*  */”往往是用来注释一段文字,当然“/*  */”也可以注释一行文字。但是符号“//”仅仅能注释一行文字,却不能注释一段文字。
       讲完注释语句,继续回到本节正题。单片机本身具备了简单的加减乘除运算能力,我们只需要通过C语言调用相关的运算语法,即可指示单片机按我们的要求进行简单的运算。至于内部具体的运算细节我们可以不管,除非是涉及到大数据的运算才需要我们额外编写算法。请先看以下的加法语法格式:
       “保存变量”=“加数1+“加数2+...+“加数N”;
        含义是:右边的“加数”与“加数”相加,并且把最终的运算结果赋值给左边的“保存变量”。注意,这里的符号“=”不是等于号的意思,而是赋值的意思。左边的“保存变量”必须是变量,不能是常量,否则编译时会报错。而右边的“加数”既可以是变量,也可以是常量,也可以是“保存变量”本身自己。多说一句,何谓变量和常量?变量就是可以在程序中被更改的,是分配的一个RAM空间。而常量往往就是数字,或者是被分配在ROM空间的一个具体数值。下面根据右边“被加数”与“加数”的不同组合,列出了加法运算的5种常用格式。
      第1种:“加数1”是常量,“加数2”是常量。比如:
unsigned char a;
a=3+15;
数字“3”和“15”都是常量。执行上述语句后,保存变量a变成了18

      第2种:“加数1”是变量,“加数2”是常量。比如:
unsigned char b;
unsigned char x=10;
b=x+15;
x是变量,“15”是常量。由于原来x变量里面的数值是10,执行上述语句后,保存变量b变成了25。而变量x则保持不变,x还是10

      第3种:“加数1”是变量,“加数2”是变量。比如:
unsigned char c;
unsigned char x=10;
unsigned char y=6;
c=x+y;
x是变量,y也是变量。由于原来x变量里面的数值是10y变量里面的数值是6,执行上述语句后,保存变量c变成了16。而变量xy则保持不变,x还是10y还是6

      第4种:“加数1”是保存变量本身,“加数2”是常量。比如:
unsigned char d=2;
d=d+18;
d=d+7;
d是保存变量,“18”是常量。这类语句有一个特点,具备了自加功能,可以更改自己本身自己的数值。比如原来保存变量d的数值是2,执行“d=d+18;”语句后,d变成了20,接着再执行完“d=d+7;”语句后,d最后变成了27

      第5种:“加数1”是保存变量本身,“加数2”是变量。比如:
unsigned char e=2;
unsigned char x=10;
unsigned char y=6;
e=e+x;
e=e+y;
e是保存变量,xy都是变量。这类语句有一个特点,具备了自加功能,可以更改自己本身自己的数值。比如原来保存变量e的数值是2,执行“e=e+x;”语句后,e变成了12,接着再执行完“e=e+y;”语句后,e最后变成了18

       现在编写一个程序来练习上述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=2;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为2.
  9.   unsigned char e=2;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为2.
  10.         
  11.   unsigned char x=10;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为10.
  12.   unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        

  13.   //第1种:“加数1”是常量,“加数2”是常量。
  14.         a=3+15;
  15.         
  16.         
  17.         //第2种:“加数1”是变量,“加数2”是常量。
  18.         b=x+15;
  19.         
  20.         
  21.         //第3种:“加数1”是变量,“加数2”是变量。
  22.   c=x+y;
  23.         
  24.         
  25.   //第4种:“加数1”是保存变量本身,“加数2”是常量。
  26.   d=d+18;
  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,e5个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:
变量a为18
变量b为25
变量c为16
变量d为27
变量e为18

     下节预告:加法的连写和自加运算的简写。
(未完待续)



乐于分享,勇于质疑!
26#
 楼主| 发表于 2015-5-10 07:53:28 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:29 编辑

第十七节:连加以及自加运算的简写。
       上一节我列举的加法例子中,右边的加数个数都是两个。实际上,C语言规则没有限制加数的个数,它的通用格式如下:
       “保存变量”=“加数1+“加数2+...+“加数N”;
       当右边的加数个数超过两个的时候,这种情况就是我所说的“连加”,每个加数的属性没有限定,可以是常量,也可以是变量。比如:
a=1+69+102;     //加数全部是常量。
b=q+x+y+k+r;    //加数全部是变量。
c=3+x+y+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;
        这些例子都是很常规的自加简写,再跟大家讲一种很常用的特殊简写。当右边只有2个加数,当一个加数是“保存变量”,另一个是常数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=5;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为5.
  9.   unsigned char e=5;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为5.
  10.   unsigned char f=5;       //定义一个变量f,并且分配了1个字节的RAM空间。初始化默认为5.
  11.         unsigned char g=5;       //定义一个变量g,并且分配了1个字节的RAM空间。初始化默认为5.        
  12.   unsigned char h=5;       //定义一个变量h,并且分配了1个字节的RAM空间。初始化默认为5.

  13.         
  14.   unsigned char q=1;    //定义一个变量q,并且分配了1个字节的RAM空间。初始化默认为1.
  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.   unsigned char r=8;     //定义一个变量r,并且分配了1个字节的RAM空间。初始化默认为8.
  19.         
  20.         
  21.          //第1个知识点:连加。
  22.    a=1+69+102;     //加数全部是常量。a的结果为:172。
  23.    b=q+x+y+k+r;    //加数全部是变量。b的结果为:20。
  24.    c=3+x+y+5+k;   //加数有的是常量,有的是变量。c的结果为:19。

  25.          //第2个知识点:自加的常规格式。
  26.    d+=6;  //相当于d=d+6;  d的结果为:11。
  27.    e+=x;  //相当于e=e+x;  e的结果为:8。
  28.    f+=18+y+k; //相当于f=f+18+y+k;  f的结果为:31。
  29.          
  30.          
  31.          //第3个知识点:自加的特殊格式。
  32.    g++;  //相当于g=g+1或者g+=1;  g的结果为:6。
  33.    ++h;  //相当于h=h+1或者h+=1;  h的结果为:6。




  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,h8个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。上坚鸿51学习板观察程序执行的结果如下:

变量a为172
变量b为20
变量c为19
变量d为11
变量e为8
变量f为31
变量g为6
变量h为6
     下节预告:加法的溢出和优先级
(未完待续)




乐于分享,勇于质疑!
27#
 楼主| 发表于 2015-5-16 23:41:15 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 18:29 编辑

第十八节:加法运算的溢出。
       我前面介绍的三种数据类型unsigned char ,unsigned int ,unsigned long,都是有最大范围限制的,它们最大范围分别是255,65535,4294967295,如果加法运算的结果超过了参与运算的变量本身,会出现什么结果,有什么规律,这就是本节要讲解的溢出问题。
    (1)何谓溢出?比如以下例子:
unsigned char a;
a=0x8536;
分析:
因为aunsigned char变量,位数是8位,也就是1个字节,而0x853616位,2个字节,这种情况下,把0x8536赋值给单字节变量a,变量a只能接收到最低位的一个字节0x36,而高位字节的0x85就被丢失了,这个就是本节所说的“溢出”了。
    (2)再看一个例子如下:
unsigned char b=0xff;
b=b+1;
分析:
b默认值是0xff,再加1后,变成了0x0100保存在一个隐藏的中间变量,然后再把这个中间变量赋值给单字节变量bb只能接收到低位字节0x00,所以运算后b的数值由于溢出变成了0x00
    (3)再看一个例子如下:
unsigned char c=0xff;
c=c+2;
分析:
c默认值是0xff,再加2后,变成了0x0101保存在一个隐藏中间变量,然后再把这个中间变量赋值给单字节变量cc只能接收到低位字节0x01,所以运算后c的数值由于溢出变成了0x01
    (4)再看一个例子如下:
Unsigned int d=0xfffe;
d=d+5;
分析:
d默认值是0xfffe,再加5后,变成了0x10003保存在一个隐藏中间变量,由于这个隐藏的中间变量是unsigned int类型,只能保存2个字节的数据,所以在中间变量这个环节就溢出了,实际上隐藏的中间变量只保存了0x0003,然后再把这个中间变量赋值给双字节变量dd理所当然也是0x0003
    (5)再看一个例子如下:
unsigned long e=0xfffffffe;
e=e+5;
分析:
e默认值是0xfffffffe,再加5后,变成了0x100000003保存在一个隐藏中间变量,由于这个隐藏的中间变量是unsigned long类型,只能保存4个字节的数据,所以在中间变量这个环节就溢出了,实际上隐藏的中间变量只保存了0x00000003,然后再把这个中间变量赋值给4字节变量ee理所当然也是0x00000003
       现在编写一个程序来练习上述前面4个例子,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

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

  9.         
  10.    a=0x8536;
  11.    b=b+1;
  12.    c=c+2;
  13.    d=d+5;




  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学习板上观察a,b,c,d4个变量的十六进制?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量,只要按住S9按键不放,此时显示的就是该变量的十六进制。上坚鸿51学习板观察程序执行的结果如下:

变量a为0x36。
变量b为0x00。
变量c为0x01。
变量d为0x0003。

        这一节提到了一个“隐藏中间变量”的概念,这个神秘的“隐藏中间变量”到底是unsigned int类型还是unsigned long类型?有什么规律?如果运算中存在多种不同变量类型该怎么办,实际应用中有解决的办法吗?预知详情,请看一节内容。
       下节预告:加法运算中,神秘中间变量的类型以及解决“掺杂多种变量类型”的办法。
(未完待续)




乐于分享,勇于质疑!
28#
 楼主| 发表于 2015-5-22 05:45:55 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-5-22 05:48 编辑

第十九节:加法运算中,神秘中间变量的类型以及解决“掺杂多种变量类型”的办法。
       在开始本节内容之前,先告诉大家前面第十一节内容有一处笔误,unsigned long的数据长度应该是4个字节,而不是3个字节。
上一节提到了一个“隐藏中间变量”的概念,两个加数相加,其结果先保存在一个“隐藏中间变量”里,然后再把这个“隐藏中间变量”赋值给左边的“保存变量”。这里的“隐藏中间变量”到底是unsigned int类型还是unsigned long类型?为了研究它的规律,在keil自带的C51编译环境下,我专门编写了好几个测试程序来观察实际运行的结果。
       “保存变量”=“加数1+“加数2;
       我测试的程序如下:
    (1)“保存变量”为 unsigned int类型,“加数1”为unsigned char类型,“加数2”为unsigned char 类型。
  1. unsigned int a;
  2. unsigned char x=0x12;
  3. unsigned char y=0xfe;
  4. a=x+y;
复制代码

运行结果:a等于0x0110
分析过程:两个char类型的数相加,当运算结果大于char本身时,并没有发生溢出现象,int型的“保存变量”a最终得到了完整的结果。
初步结论:这种情况,“隐藏中间变量”应该为unsigned int 类型。
    (2)“保存变量”为 unsigned long类型,“加数1”为unsigned int类型,“加数2”为unsigned char 类型。
  1. unsigned long a;
  2. unsigned int x=0xfffe;
  3. unsigned char y=0x12;
  4. a=x+y;
复制代码

运行结果:a等于十六进制的0x0010
分析过程:一个int类型的数与一个char类型的数相加,当运算结果大于其中最大加数int类型本身时,本来以为运算结果应该是long类型的0x00010010,结果是int类型的0x0010,发生了溢出现象。
初步结论:这种情况,“隐藏中间变量”应该为unsigned int 类型。
     (3)“保存变量”为 unsigned long类型,“加数1”与“加数2”都为常量。
  1. unsigned long a;
  2. a=50000+50000;
复制代码

运行结果:a等于100000
分析过程:int的最大数据范围是65535,而两个常量相加,其结果超过了65535还能完整保存下来。
初步结论:这种情况,“隐藏中间变量”等于左边的“保存变量”类型。
     (4)“保存变量”为 unsigned long类型,“加数1”为unsigned int类型,“加数2”为常量。
  1. unsigned long a;
  2. unsigned long b;
  3. unsigned  int x=50000;
  4. a=x+30000;
  5. b=x+50000;
复制代码


运行结果:a等于14464,b等于100000
分析过程:本来以为a应该等于80000的,结果是14464发生了溢出。而b100000没有发生溢出。
初步结论:这是一种很怪异的现象,为什么同样的类型,因为常量的不同,一个发生了溢出,另外一个没有发生溢出?这时的“隐藏中间变量”到底是int类型还是long类型我无法下结论。
       经过上述简单的测试,我发现规律是模糊的,模糊的规律就不能成为规律。如果真要按这种思路研究下去,那真是没完没了,因为还有很多情况要研究,当超过3个以上加数相加,同时存在long,int,char,常量这4种类型时又是什么规律?在不同的C编译器里又会是什么现象?即使把所有情况的规律摸清楚了又能怎么样,因为那么繁杂很容易忘记导致出错。有什么解决的办法吗?现在跟大家分享一种很简单的解决办法。
       当遇到有争议的问题时,还有一种解决思路是:与其参与争议越陷越深,还不如想办法及时抽身绕开争议。在上述运算中,只要经过简单的变换,让它们遵循“所有参与运算的变量,左边的变量类型必须跟右边的保存变量类型一致”这个原则,那么就不会存在这些争议了。
     (5)比如上述第(4)个例子,其转换方法如下:
  1. unsigned long a;
  2. unsigned long b;
  3. unsigned  int x=50000;
  4. Unsigned  long t;  //多增加一个long类型的变量,用来变换类型
  5. t=0;  //把变量的高位和低位全部清零。
  6. t=x;   //把x的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
  7. a=t+30000;
  8. b=t+50000;
复制代码

运行结果:a等于80000,b等于100000。都没有发生溢出。
      (6)比如上述第(2)个例子,其转换方法如下:
  1. unsigned long a;
  2. unsigned int x=0xfffe;
  3. unsigned char y=0x12;
  4. unsigned  long t;  //多增加一个long类型的变量,用来变换类型。
  5. unsigned  long r;  //多增加一个long类型的变量,用来变换类型。
  6. t=0;//把变量的高位和低位全部清零。
  7. t=x;   //把x的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
  8. r=0;  //把变量的高位和低位全部清零。
  9. r=y   //把y的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
  10. a=t+r;
复制代码

运行结果:a等于十六进制的0x00010010,没有发生溢出现象。

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



乐于分享,勇于质疑!
29#
 楼主| 发表于 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。
     下节预告:减法的连写和自减运算的简写。
(未完待续)

乐于分享,勇于质疑!
30#
 楼主| 发表于 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。
       下节预告:减法运算的溢出。
(未完待续)

乐于分享,勇于质疑!
31#
 楼主| 发表于 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)

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

乐于分享,勇于质疑!
32#
 楼主| 发表于 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种常见格式。
(未完待续)
乐于分享,勇于质疑!
33#
 楼主| 发表于 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。
     下节预告:乘法连写的简写。
(未完待续)

乐于分享,勇于质疑!
34#
 楼主| 发表于 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。
     下节预告:乘法运算的溢出。
(未完待续)



乐于分享,勇于质疑!
35#
 楼主| 发表于 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)。

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

乐于分享,勇于质疑!
36#
 楼主| 发表于 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。
     下节预告:整除求余的运算。
(未完待续)

乐于分享,勇于质疑!
37#
 楼主| 发表于 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。
     下节预告:利用“整除求商求余”来提取一个数的个十百千位。
(未完待续)

乐于分享,勇于质疑!
38#
 楼主| 发表于 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。
     下节预告:逻辑运算符的“与”运算。
(未完待续)

乐于分享,勇于质疑!
39#
 楼主| 发表于 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)。
        下节预告:逻辑运算符的“或”运算。
(未完待续)

乐于分享,勇于质疑!
40#
 楼主| 发表于 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)。
      下节预告:逻辑运算符的“异或”运算。
(未完待续)

乐于分享,勇于质疑!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-5-6 21:58 , Processed in 0.379453 second(s), 16 queries .

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