引言
    8051内核单片机是一种通用单片机,在国内占有较大的市场份额。在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功。由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用与标准C有所不同。正确地使用变量,有利于获得高效的目标代码。下面详细介绍Keil C51中变量的使用方法。

1 CPU存储结构与变量的关系
    变量都需要有存储空间,存储空间的不同使得变量使用时的工作效率也不同。
    标准C的典型运行环境是8086(含IA-32系列)内核,其存储结构是CPU内部有寄存器,外部有存储器,寄存器的访问速度大大高于存储器的访问速度。在标准C中,不加特别定义的变量是放在存储器中的,使用register可以强制变量存储在寄存器中,对于使用特别频繁且数量不多的变量可以选用这种存储模式,以获得更高的工作效率。
    相比之下,51内核单片机的存储结构则显得有些怪异,它的存储空间有3个:程序存储器空间(64 KB含片内、片外)、片外数据存储器空间(64KB)、片内数据存储器及特殊功能寄存器空间。它没有真正意义上的寄存器,它的寄存器其实是片内数据存储器(如R0~R7)和特殊功能寄存器(如A、B等)中的一部分。因此,在Keil C51中使用变量就和标准C有很大不同。

2 Keil C51变量分析
    Keil C51支持标准C原有的大多数变量类型,但为这些变量新增了多种存储类型,也新增了一些标准C没有的变量。
2.1 Keil C51新增的变量存储类型
    Keil C51中定义变量的格式如下:
    [存储种类]数据类型[存储类型]变量名表;
    其中,[存储类型]是标准C中没有的,[存储类型]共有6种,分别介绍如下:
    ①data。将变量存储在片内可直接寻址的数据存储器中。使用这种存储模式,目标代码中对变量的访问速度最快。
    ②bdata。将变量存储在片内可位寻址的数据存储器中。在目标代码中变量可以方便地进行位处理,在不进行位处理时与data相同。
    ③idata。将变量存储在片内间接寻址的数据存储器中。在52内核中,当片内直接寻址数据存储器不够用时,可以使用128字节间接寻址数据存储器,访问速度一般较data要慢一些,但具有最大的片内数据存储器空间;在51内核中因无单独的间接寻址数据存储器区,idata与data无区别。
    ④xdata。将变量存储在片外数据存储器中。目标代码中只能使用“MOVX A,@DPTR”和“MOVX@DPTR,A”指令访问变量,访问速度最慢,但存储空间最大(64KB)。
    ⑤pdata。将变量存储在片外数据存储器中的第一页(00H~FFH)中。目标代码中可以使用“MOVX A,@Ri”和“MOVX@Ri,A”指令访问变量,访问速度与xdata相同,存储空间为256字节。
    ⑥code。将变量存储在程序存储器中。目标代码中只能使用MOVC指令访问变量,因变量存储在程序存储器中,具有非易失性且为只读。
2.2 Keil C51新增的指针变量存储类型
    Keil C51中的指针变量形式如下:
    数据类型[数据存储类型]*[指针存储类型]标识符;
    其中,[数据存储类型]和[指针存储类型]都是标准C中没有的。[数据存储类型]定义数据(即寻址对象)存储的空间,[指针存储类型]定义指针自身存储的空间。若不使用[数据存储类型],则指针为一般指针,占用3个字节;若使用[数据存储类型]则指针为基于存储器的指针,占用1~2个字节。
2.3 Keil C51新增的变量类型
    bit:位变量。存储在片内数据存储器的可位寻址字节(20H~2FH)的某个位上,这个变量在实时控制中具有很高的实用价值。
    sfr:特殊功能寄存器变量。存储在片内特殊功能寄存器中,用来对特殊功能寄存器进行读写操作。
    sbit:特殊功能寄存器位变量。存储在片内特殊功能寄存器的可位寻址字节(地址可以被8整除者)的某个位上,用来对特殊功能寄存器的可位寻址位进行读写操作。
    sbitl6:16位特殊功能寄存器变量。存储在片内特殊功能寄存器的连续2个字节的低地址上,这个变量类型很少使用。
以上这些Keil C51中新增的变量类型,不支持数组和指针操作。
  3 Keil C51中使用变量存储模式的必要性
    在Keil C51中,变量的存储模式是一个可选项,如果不使用这个选项,则Keil C51在编译时自动进行优选分配。但这种处理方法有以下缺点:
    ①系统不知道各种变量的使用频度,有可能对使用频度高的变量使用了访问速度慢的片外存储方式,而对使用频高的变量使用了片内存储方式,使得程序的运行效率降低;
    ②在使用指针寻址时,由于不知道寻址对象的存储方式,只好使用一般指针,在Keil C51中一般指针要多占用1~2个字节,并且使用时还要对存储方式进行判断,增加了寻址操作时间。
    如果能够在定义变量的同时定义其存储类型,可以高效地使用51内核单片机的存储空间,获得高质量的目标代码。

4 Keil C51变量的使用方法
4.1 全局变量和静态局部变量
    全局变量一般会在多个函数中被使用,并在整个程序运行期间内有效,静态局部变量虽然只在一个函数中使用,但也是在整个程序运行期间有效。对于这些变量,应尽量选择data型,这样在目标代码中就可以用直接寻址指令访问,获得最高的访问速度,提高程序的工作效率。例如一个保存人数的全局变量n_g,在多个函数中都被经常用到,可以这样定义:
    unsigned int data n_g;//对n_g赋值时使用“MOV XXH,……”指令
4.2 数组(包括全局和局部)
    定义数组一般用idata存储类型,在目标代码中使用“MOV@Ri”指令进行间接寻址。如果因数组元素过多而在编译时报错,可以改用pdata和xdata存储类型。
    数组定义为data存储类型意义不大,因为既然使用数组,就是希望能够根据某一自变量访问数组元素。如定义X[100],一般都是为了能够使用X(i是一个变量)来访问,这样在目标代码中就必须使用问接寻址,所以数组没有必要使用data存储类型,即便使用了data存储类型,在目标代码中也仍然要用间接寻址指令。数组定义成idata存储类型,在使用52内核且片内数据存储器不够时,会使用只能间接寻址的片内数据存储空间。这样,既不能降低处理速度,又扩大了可使用的存储空间。
4.3 供查表用的数据
    这类数据的特点是需要始终保持不变,且使用时只读,因此应定义为code型。例如一个字形表:
    <ignore_js_op> 
    全局或局部code型变量在存储时无区别。
4.4 非静态局部变量
    非静态局部变量仅在某一函数内使用,退出该函数时变量也被释放。
    若系统使用small存储模式,对于这些变量可以不加存储说明,由编译软件自行按最优原则决定,因为仅在函数内使用的非静态局部变量,有可能使用工作寄存器R0~R7,这样会更快速和更节省存储空间。例如:
    unsigned char i,j; //系统尽可能会用R0~R7存储i和j
    若系统使用了compact或large存储模式,则应将这些变量定义为data存储模式,以防系统自行决定时被定义为pdagta或xdata模式而降低工作效率。
4.5 指针
    如前所述,定义指针变量时有2个存储类型:数据存储类型,说明被寻址对象的存储类型;指针存储类型,说明指针自身的存储类型。当数据存储类型为xdata时,指针自身占用2个字节;当数据存储类型为pdata以及idata等片内存储类型时,指针自身占用1个字节;若不说明数据存储类型,指针自身就要占用3个字节。因此,在KeilC51中使用指针时,应尽量定义数据存储类型,但要特别注意指针中的数据存储类型与被寻址对象的存储类型必须一致。指针都是频繁使用的,它要不断被设置、修改和使用,因此它自身的存储类型应选择data型。例如定义一个数组时就同时定义其存储类型,以后用指针对其寻址时就将数组的存储类型添加到指针的数据类型中。方法如下:
      <ignore_js_op> 
  4.6 二义性变量
    在标准C中如果要使用一个二义性变量,只能用枚举类型。如:
    <ignore_js_op> 
    以上程序在Keil C51中使用时,变量t虽然仅有0和1两种状态,但在目标代码中仍占用一个字节。此处理方法既浪费存储资源,又延长了处理时间,这对于8086内核算不上多大问题,但在资源有限、运行速度不高的51内核中就不能不考虑了。在Keil C51中可使用以下方法:
    <ignore_js_op> 
    这两种方式效果是完全相同的,但在目标代码中变量t仅占用1位(即1/8字节),而且因为51内核单片机指令系统中有位处理指令,生成的目标代码占用内存少、运行速度快。
4.7 特殊功能寄存器变量(包括位变量)
    特殊功能寄存器中,累加器A、寄存器B、堆栈指针SP和数据指针DPTR是归系统使用的,在C51中不提供给用户。其他的特殊功能寄存器都可以用sfr定义成变量,其中地址可以被8整除者的各位,还可以用bsfr定义成位变量。访问这些变量,就可以对特殊功能寄存器及其可以位寻址的各位进行读写,达到操作单片机内部各硬件的目的。对于标准的51内核单片机,头文件reg51.h、reg52.h或其他头文件中已对这些特殊功能寄存器变量作了定义,用户可以用#include将此头文件包含进来,然后就可以使用了。现在很多51内核兼容型单片机扩展了更多的特殊功能寄存器,这些就需要用户自行定义,具体方法可参考器件的使用说明。
4.8 外部数据存储器变量
    若设置成pdata和xdata存储类型,将把变量存储在片外数据存储器中。这两种存储类型的访问速度最慢,非迫不得已不要使用。在使用这两种存储类型时,注意尽量只用它保存原始数据或最终结果,尽量减少对其访问的次数,需要频繁访问的中间结果不要用它。
4.9 用外部数据存储器地址扩展的其他硬件
    在单片机外部扩展的其他硬件,一般都借用外部数据存储器地址,表现为外部数据存储器单元形式。对于这些硬件,可以用指针进行读写操作。例如:
    <ignore_js_op> 
结语
    Keil C51中的变量增加了存储类型,在使用时而显得比标准C稍微复杂。在Keil C51中,变量的存储类型不同,访问变量所需要的时间也不同,由于C51内核单片机资源少、速度慢,变量存储类型对系统工作速度的影响不可忽视。在了解变量与单片机存储结构关系的基础上,根据程序对变量的使用要求,合理地选择变量的存储类型,可以在相同的硬件上获得更高的工作效率。

[51单片机] Keil C51中变量的使用方法详解的更多相关文章

  1. Keil C51中变量的使用

    引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额.在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功.由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用 ...

  2. PHP 中 16 个魔术方法详解

    PHP 中 16 个魔术方法详解   前言 PHP中把以两个下划线__开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __constru ...

  3. php_DWZ-JUI中碰到的问题解决方法详解(thinkphp+dwz)

    原文:php_DWZ-JUI中碰到的问题解决方法详解(thinkphp+dwz) 折腾了两天,dwz删除后,数据不能自动刷新,解决方案,直接看图  . 1. 删除.修改状态后无法刷新记录: 在dwz. ...

  4. python中requests库使用方法详解

    目录 python中requests库使用方法详解 官方文档 什么是Requests 安装Requests库 基本的GET请求 带参数的GET请求 解析json 添加headers 基本POST请求 ...

  5. Keil C51中变量和函数的绝对地址定位问题

    1.变量绝对地址定位 1) 在定义变量时使用 _at_ 关键字加上地址就可. unsigned char idata myvar _at_ 0x40;  把变量 myvar 定义在 idata 的 0 ...

  6. iOS 判断数组array中是否包含元素a,取出a在array中的下标+数组方法详解

    目前找到来4个解决办法,第三个尤为简单方便 NSArray * arr = @["]; //是否包含 "]) { NSInteger index = [arr indexOfObj ...

  7. HTTP 协议中 GET 和 POST 方法详解

    GET请求报文分析 1.请求行 请求方法 GET(描述该请求采用了什么请求方法),HTTP 1.0 和 1.1 协议中共包含10种请求方法.不过 HTTP 1.1 中只有8种方法. URI 请求WEB ...

  8. php中static静态变量的使用方法详解

    php中的变量作用范围的另一个重要特性就是静态变量(static 变量).静态变量仅在局部函数域中存在且只被初始化一次,当程序执行离开此作用域时,其值不会消失,会使用上次执行的结果.     看看下面 ...

  9. 使用@符号让C#中的保留字做变量名的方法详解

    原来还有一种办法就是加@符号(看了@符号的作用又多了一个): 复制代码代码如下: class @int    {        static void Main(string[] args)      ...

随机推荐

  1. 7-[CSS]-css介绍,引入方式

    1.CSS介绍:层叠样式表(Cascading Style Sheets) 我们为什么需要CSS? 使用css的目的就是让网页具有美观一致的页面,另外一个最重要的原因是内容与格式分离 在没有CSS之前 ...

  2. doc2vec使用笔记

    #!/usr/bin/env Python # coding:utf-8 #improt依赖包 # import sys # reload(sys) # sys.setdefaultencoding( ...

  3. JZOJ5371 组合数问题

    Description 定义"组合数"S(n,m)代表将n 个不同的元素拆分成m 个非空集合的方案数. 举个例子,将{1,2,3}拆分成2 个集合有({1},{2,3}),({2},{1,3}),({ ...

  4. ASP.NET5之客户端开发:Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用

    Grunt和Gulp是Javascript世界里的用来做自动压缩.Typescript编译.代码质量lint工具.css预处理器的构建工具,它帮助开发者处理客户端开发中的一些烦操重复性的工作.Grun ...

  5. 自适应浏览器分辨率的javascript函数[转]

    function changeWidth(now,target) { //now是现在代码所适应的宽度,如800:target是想要达到的显示器分辨率宽度var widthStr; var flag ...

  6. 转载Linux下开启MySQL日志

    转载https://blog.csdn.net/weixin_38187469/article/details/79273962 开启mysql日志   1.查看日志是否启用 mysql> sh ...

  7. Linux(centos7)之更换安装python3(二)

    Linux不比window好安装python,折腾了好久,终于成功安装上了python,window上一个安装包完事,可惜Linux上python版本太低不好使,还要更换为3版本,百度了好久,教程上总 ...

  8. 高速下载百度网盘的方法,只需要一个软件【win/mac/linux】

    论坛里差不多全部都是用百度云的,但是官方百度云的限速,大家都懂的,下面教大家使用一个软件满速下载,不用油猴 1,软件支持mac,win64/32,linux.Linux只有自己编译使用,我也不会哈哈哈 ...

  9. Laya 1.x 按文件夹TS代码合并

    Laya 1.x 使用TS开发时,经常会碰到代码文件太多,加载index.html时时间太长的问题.Laya编辑器貌似没有自带JS代码合并的功能.基于Laya去实现JS合并需要修改编辑器源码,合并JS ...

  10. jmeter线程组介绍

    Jmeter中的测试计划是一直有的,但可以在右侧修改名字,要开始做具体测试设计前,都需要在测试计划下边添加一个线程组,添加路径为鼠标捕获测试计划后,点击鼠标右键->添加->Threads( ...