当前位置:首页 > 单片机 > 单片机
[导读]常量和符号常量在程序运行过程中,其值不能被改变的量称之为常量。常量分为不同的类型,有整型常量如 1、2、3、100;浮点型常量 3.14、0.56、-4.8;字符型常量?a?、?b?、?0?;字符串常量“a”、“abc”、“1234”、“

常量和符号常量


在程序运行过程中,其值不能被改变的量称之为常量。常量分为不同的类型,有整型常量如 1、2、3、100;浮点型常量 3.14、0.56、-4.8;字符型常量?a?、?b?、?0?;字符串常量“a”、“abc”、“1234”、“1234abcd”等。


细心的同学会发现,整型和浮点型常量我们直接写的数字,而字符型常量用单引号来表示一个字符,用双引号来表示一个字符串,尤其大家要注意?a?和“a”是不一样的,这个等会我们要详细介绍。


常量一般有两种表现形式:

直接常量:直接以值的形式表示的常量称之为直接常量。上述举例这些都是直接常量,直接写出来了。

符号常量:用标识符命名的常量称之为符号常量,就是为上面的直接常量再取一个名字。使用符号常量一是方便理解,提高程序可读性,更重要的是方便程序的后续维护,习惯上符号常量我们都用大写字母和下划线来命名。


比如,我们可以把 3.14 取名为 PI(即π)。再比如,我们上节课的串口程序,我们用的波特率是 9600,如果用符号常量来进行提前声明的话,那我们要修改成其它速率的话,就不用在程序中找 9600 修改了,直接修改声明处就可以了,两种方法举例说明。用 const 声明。比如我们在程序开始位置定义一个符号常量 BAUD。


定义形式是:

const 类型 符号常量名字=常量值;

const unsigned int BAUD = 9600; /*注意结尾有个分号*/

我们就可以在程序中直接把 9600 改成 BAUD,这样我们如果要改波特率的话,直接在程序开头位置改一下这个值就可以了。用预处理命令#define 来完成,预处理命令我们先来认识#define。


定义形式是:

#define 符号常量名 常量值

#define BAUD 9600 /*注意结尾没有分号*/

这样定义以后,只要在程序中出现 BAUD 的话,意思就是完全替代了后边的 9600 这个数字。


不知大家是否记得,我们之前定义数码管真值表的时候,用了一个 code 关键字。

unsigned char code LedChar[] = { //数码管显示字符转换表

0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,

0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E

};

我们当时说加了 code 之后,这个真值表的数据只能被使用,不能被改变,如果我们直接写 LedChar[0] = 1;这样就错了。实际上 code 这个关键字是 51 单片机特有的,如果是其它类型的单片机我们只需要写成 const unsigned char LedChar[]={}就可以了,自动保存到 FLASH里,而 51 单片机只用 const 而不加 code 的话,这个数组会保存到 RAM 中,而不会保存到FLAHS 中,鉴于此,在 51 这个体系下,const 反倒变得不那么重要了,它的作用被 code 取代了,这里大家知道这么回事即可。


我们来对各种类型的常量做进一步说明。


整型常量和浮点型常量就没多少可说的了,之前我们应用的都很熟练了,整型直接写数字就是十进制如 128,前边 0x 开头的表示是十六进制 0x80,浮点型直接写带小数点的数据就可以了。


字符型常量是由一对单引号括起来的单个字符。它分为两种形式,一种是普通字符,一种是转义字符。


普通字符就是那些我们可以直接书写直接看到的有形的字符,比如阿拉伯数字 0~9,英文字符 A~z,以及标点符号等。它们都是 ASCII 码表中的字符,而它们在单片机中都占用一个字节的空间,其值就是对应的 ASCII 码值。比如?a?的值是 97,?A?的值是 65,?0?的值是48,如果定义一个变量 unsigned char a = ?a?,那么变量 a 的值就是 97。


除了上述这些字符之外,还有一些特殊字符,它们一些是无形的,像回车符、换行符这些都是看不到的,还有一些像?”这类字符它们已经有特殊用途了,想象一下如果写 '''觉得编译器会怎么去解释呢。针对这些特殊符号,为了可以让它们正常进入到我们的程序代码中,C 语言就规定了转义字符,它是以反斜杠()开头的特定字符序列,让它们来表示这些特殊字符,比如我们用n 来代表换行。我们用一个简单表格来说明一下常用的转义字符的意思,如表 12-2 所示。


表 12-2 常用转义字符及含义

字符形式含义

n换行

t横向跳格(相当于 Tab)

v竖向跳格

b退格

r光标移到行首

\反斜杠字符??

?单引号字符

”双引号字符

f走纸换页

空值

表格不需要大家记住,用到了,过来查就可以了。


字符串常量是用双引号括起来的字符序列,一般我们都称之字符串。如“a”、“1234”、“welcome to www.kingst.org”等都是字符串常量。字符串常量在内存中按顺序逐个存储字符串中的字符的 ASCII 码值,并且特别注意,最后还有一个字符??,??字符的 ASCII 码值是 0,它是字符串结束标志,在写字符串的时候,这个??是隐藏的,我们看不到,但是实际却是存在的。所以“a”就比?a?多了一个 ??,“a”的就占了 2 个字节,而 ?a?只占一个字节。


还有一 个地 方要注 意, 就是字 符串 中的空 格, 也是一 个字 符,比 如 “welcome to www.kingst.org”一共占了 26 个字节的空间。其中 21 个字母,2 个?.?,2 个 ? ?(空格字符)以及一个??。

字符和字符串数组实例


为了对比字符串、字符数组、常量数组的区别,我们写个了简单的演示程序,定义了 4个数组分别是:

unsigned char array1[] = "1-Hello!rn";

unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', 'r', 'n'};

unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};

unsigned char array4[] = "4-Hello!rn";


在串口调试助手下,发送十六进制的 1、2、3、4,使用字符形式显示的话,会分别往电脑上送这 4 个数组中对应的那个数组。我们只是在起始位置做了区分,其它均没有区别。大家可以比较一下效果。


此外还要说明一点,数组 1 和数组 4,数组 1 我们是发完整的字符串,而数组 4 我们仅仅发送数组中的字符,没有发结束符号。串口调试助手用字符形式显示是没有区别的,但是大家如果改用十六进制显示,大家会发现数组 1 比数组 4 多了一个字节? ?的 ASCII 值 00。

纯文本复制

#include

bit cmdArrived = 0; //命令到达标志,即接收到上位机下发的命令

unsigned char cmdIndex = 0; //命令索引,即与上位机约定好的数组编号

unsigned char cntTxd = 0; //串口发送计数器

unsigned char *ptrTxd; //串口发送指针

unsigned char array1[] = "1-Hello!rn";

unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', 'r', 'n'};

unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};

unsigned char array4[] = "4-Hello!rn";

void ConfigUART(unsigned int baud);

void main(){

EA = 1; //开总中断

ConfigUART(9600); //配置波特率为 9600

while (1){

if (cmdArrived){

cmdArrived = 0;

switch (cmdIndex){

case 1:

ptrTxd = array1; //数组 1 的首地址赋值给发送指针

cntTxd = sizeof(array1); //数组 1 的长度赋值给发送计数器

TI = 1; //手动方式启动发送中断,处理数据发送

break;

case 2:

ptrTxd = array2;

cntTxd = sizeof(array2);

TI = 1;

break;

case 3:

ptrTxd = array3;

cntTxd = sizeof(array3);

TI = 1;

break;

case 4:

ptrTxd = array4;

cntTxd = sizeof(array4) - 1; //字符串实际长度为数组长度减 1

TI = 1;

break;

default:

break;

}

}

}

}

/* 串口配置函数,baud-通信波特率 */

void ConfigUART(unsigned int baud){

SCON = 0x50; //配置串口为模式 1

TMOD &= 0x0F; //清零 T1 的控制位

TMOD |=

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

链表作为一种基础的数据结构,在程序设计中扮演着重要角色。掌握链表的高效操作技巧,特别是逆序、合并和循环检测,对于提升算法性能和解决复杂问题至关重要。本文将详细介绍这些操作的C语言实现,并分析其时间复杂度。

关键字: 链表 C语言

在C/C++多文件编程中,静态变量(static)与全局变量的作用域规则看似简单,实则暗藏诸多陷阱。开发者若未能准确理解其链接属性与生命周期,极易引发难以调试的内存错误、竞态条件以及维护灾难。本文将深入剖析这两类变量的作...

关键字: 静态变量 全局变量 C语言

在嵌入式系统和服务器开发中,日志系统是故障排查和运行监控的核心组件。本文基于Linux环境实现一个轻量级C语言日志库,支持DEBUG/INFO/WARN/ERROR四级日志分级,并实现按大小滚动的文件轮转机制。该设计在某...

关键字: C语言 嵌入式系统

在嵌入式系统和底层驱动开发中,C语言因其高效性和可控性成为主流选择,但缺乏原生单元测试支持成为开发痛点。本文提出一种基于宏定义和测试用例管理的轻量级单元测试框架方案,通过自定义断言宏和测试注册机制,实现无需外部依赖的嵌入...

关键字: C语言 嵌入式系统 驱动开发

在嵌入式系统开发中,实时操作系统(RTOS)的任务调度算法直接影响系统的响应速度和资源利用率。时间片轮转(Round-Robin, RR)作为一种经典的公平调度算法,通过为每个任务分配固定时间片实现多任务并发执行。本文将...

关键字: 实时操作系统 RTOS C语言

在Linux设备驱动开发中,等待队列(Wait Queue)是实现进程睡眠与唤醒的核心机制,它允许进程在资源不可用时主动放弃CPU,进入可中断睡眠状态,待资源就绪后再被唤醒。本文通过C语言模型解析等待队列的实现原理,结合...

关键字: 驱动开发 C语言 Linux

在嵌入式系统开发中,C语言与汇编的混合编程是优化性能、访问特殊指令或硬件寄存器的关键技术。然而,内联汇编的语法差异和寄存器使用规则常导致难以调试的问题。本文以ARM Cortex-M和x86架构为例,系统梳理内联汇编的核...

关键字: C语言 汇编混合编程

在计算机安全领域,缓冲区溢出攻击长期占据漏洞利用榜首。这种攻击通过向程序缓冲区写入超出其容量的数据,覆盖相邻内存区域(如返回地址),进而实现任意代码执行。本文将深入探讨栈保护机制与安全函数(如snprintf)的集成防御...

关键字: 栈保护 安全函数 C语言

在嵌入式系统和大规模数值计算等性能敏感场景中,程序优化是提升效率的关键环节。gprof作为GNU工具链中的性能分析工具,能够精准定位CPU时间消耗热点。本文通过实际案例演示gprof的三个核心使用步骤,帮助开发者快速识别...

关键字: C语言 gprof 热点函数

哈希表作为高效数据检索的核心结构,其性能高度依赖冲突解决策略。本文通过C语言实现对比链地址法与开放寻址法,揭示两种方法在内存占用、查询效率及实现复杂度上的差异,为工程实践提供量化参考。

关键字: 哈希表 链地址法 开放寻址法 C语言
关闭