独闷闷网

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

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

[复制链接]
41#
 楼主| 发表于 2015-4-10 12:09:36 | 只看该作者
第十二节:两个变量的数据交换。
为了加深理解赋值语句的一个重要特性“覆盖性”,本节利用赋值语句“=”做一个实验。要求把变量a与b的两个数据进行交换,假设a原来的数据是1,b原来的数据是5,交换数据后,a的数据应该变为5,b的数据应该变为1。
很多初学者刚看到这么简单的题目,会想当然的根据我们日常生活的思路,你把你的东西给我,我把我的东西给你,就两个步骤,so easy!请直接复制第十节的模板程序,仅修改main函数后,main函数源代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a=1;   //定义一个变量a,并且分配了一个字节的RAM空间,里面保存的数据被初始化成1.
  6.   unsigned char b=5;   //定义一个变量b,并且分配了一个字节的RAM空间,里面保存的数据被初始化成5.

  7.         b=a; //第一步:为了交换,先把a的数赋值给b。
  8.         a=b; //第二步:为了交换,再把b的数赋值给a。

  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学习板观察程序执行的结果:
       变量a的数值是1。
       变量b的数值是1。
     上述实验结果并没有达到交换数据的目的,为什么?因为赋值语句有一个重要的特性,就是覆盖性。分析如下:
         b=a; //第一步
     分析点评:执行第一步后,此时虽然b得到了a的数据1,但是b原来自己的数据5已经被覆盖丢失了!
         a=b; //第二步
     分析点评:由于b的数据在执行第一步后变成了1,执行第二步后,此时相当于把1赋值给a,并没有5!所以a和b的数据都是1,不能达到交换后“a为5,b为1”的目的。
     上述交换数据的程序宣告失败!怎么办?既然赋值语句具有覆盖性,那么两变量想交换数据,就必须借助第三方寄存,此时只需要多定义一个第三方变量t。main函数源代码如下:
  1. void main() //主程序
  2. {
  3. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  4.         
  5.   unsigned char a=1;   //定义一个变量a,并且分配了一个字节的RAM空间,里面保存的数据被初始化成1.
  6.   unsigned char b=5;   //定义一个变量b,并且分配了一个字节的RAM空间,里面保存的数据被初始化成5.
  7.   unsigned char t;     //定义一个变量t,并且分配了一个字节的RAM空间,里面默认是什么数据不重要。
  8.        
  9.         t=b; //第一步:为了避免b的数据在执行第二步后被覆盖丢失,先把b的数据寄存在第三方变量t那里。
  10.         b=a; //第二步:把a的数赋值给b,b原来的数据虽然被覆盖丢失,但是b在t变量那里有备份,再也不用担心了。
  11.         a=t; //第三步:由于此时b已经获得了a的数据,如果想交换,此时只能把b在t变量里的备份赋值给a,而不能用b。

  12.   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  13.   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示

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

  22. }
复制代码

       上坚鸿51学习板观察程序执行的结果:
       变量a的数值是5。
       变量b的数值是1。
      交换成功!
     
       下节预告:二进制与字节单位。
(未完待续)

乐于分享,勇于质疑!
42#
 楼主| 发表于 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类型。
       下节预告:二进制与十六进制。
(未完待续)



乐于分享,勇于质疑!
43#
发表于 2015-4-18 13:49:34 | 只看该作者
鸿哥基础篇,写程序模块化教学可以吗。谢谢
乐于分享,勇于质疑!
44#
发表于 2015-4-18 19:47:00 | 只看该作者
继续学习   
乐于分享,勇于质疑!
45#
 楼主| 发表于 2015-4-19 01:39:34 | 只看该作者
大耳怪 发表于 2015-4-18 13:49
鸿哥基础篇,写程序模块化教学可以吗。谢谢

暂时不会讲这些。以后再看情况。
乐于分享,勇于质疑!
46#
 楼主| 发表于 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
       下节预告:十进制与十六进制。
(未完待续)





乐于分享,勇于质疑!
47#
 楼主| 发表于 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”和等于关系符“==”。
(未完待续)





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

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



乐于分享,勇于质疑!
49#
发表于 2015-5-4 13:02:02 | 只看该作者
学习了,赞一个
乐于分享,勇于质疑!
50#
 楼主| 发表于 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
     下节预告:加法的溢出和优先级
(未完待续)




乐于分享,勇于质疑!
51#
 楼主| 发表于 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类型?有什么规律?如果运算中存在多种不同变量类型该怎么办,实际应用中有解决的办法吗?预知详情,请看一节内容。
       下节预告:加法运算中,神秘中间变量的类型以及解决“掺杂多种变量类型”的办法。
(未完待续)




乐于分享,勇于质疑!
52#
 楼主| 发表于 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,没有发生溢出现象。

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



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

使用道具 举报

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。
     下节预告:减法的连写和自减运算的简写。
(未完待续)

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

乐于分享,勇于质疑!
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)

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

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

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



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

本版积分规则

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

GMT+8, 2024-11-29 16:45 , Processed in 0.432504 second(s), 15 queries .

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