《嵌入式C编程:PIC单片机和C编程技术与应用》一2.2 C常量语法-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

《嵌入式C编程:PIC单片机和C编程技术与应用》一2.2 C常量语法

简介:

本节书摘来自华章出版社《嵌入式C编程:PIC单片机和C编程技术与应用》一书中的第2章,第2.2节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.2 C常量语法

2.2.1 二进制

需要多位(bit)二进制数才能表示一个大于1的值,具体需要多少位,则取决于数字大小。一个8位的二进制数(一个字节)可以表示256个数字(0~255)。
16位的二进制数可以表示从0到65?535的值。如果使用一个字节来传输信息,一共可以传输256种可能的组合,足以表示十进制数、大小写字母,以及其他一些符号。一般情况下,使用ASCII(美国信息交换标准码)编码规范来表示这些字符。
二进制数第0位的权值是2的0次方,第1位的权值是2的1次方,第2位的权值是2的2次方,以此类推。如果第0位是1,那么它的值是1×20 = 1;如果是0,它的值则是0×20 = 0。如果第1位是1,那么它的值是1×21=2;如果是0,它的值则是0×21 = 0。如果第3位是1,那么它的值是1×23=8;如果是0,它的值则是0×23 = 0。
对于16位的二进制数来说,第0位是最低位(least significant bit),第15位是最高位(most significant bit)。图2-2给出了每一位的权值(也就是说如果该位为1,其他位为0)所代表的十进制数。在所有PIC相关的文档中,第0位指的都是最低位。这也是最常见的表示法,但并不是所有的微控制器都是如此。


04c0405bfff27cf0574f2ce9b6e8f2a0f50efa27

图2-2 二进制数转十进制数的权值
将16位二进制数每一位的值乘以其权值,相加之后就可以将其转换成十进制数(参见图2-3)。
读者可以试着计算下列二进制数字的值:
0000
0001
0010
0011
0100
0101
……


c4edc2d0678c9f6524b449614d3f8831ef1d29bd

图2-3 二进制转十进制示例
如果将这些二进制数看成是汽车的里程表,但是上面只有0和1两个数字。当1翻转成0时,就会进位。这就是数制中的基。
C语言中,在数字前加上0b表示二进制数。例如,给x赋值为6,用二进制表示就是:


036c1ff17632144bff6ea3d6082b5e8663f8971d

2.2.2 十进制

十进制就是我们经常使用的计数方式。每个数位可以使用0~9这十个符号表示不同的数值。C语言中我们这样使用它:


fe639e47a81dbf24fcc5a6848033644a1dc24e27

稍后再来介绍“类型”。这里我们注意到,编译器将会把123当作1字节来处理,因为一个字节足以存储123。如果由于某种原因,程序员需要编译器将其当作两个字节,那么可以在数字后加上L(long):


6ad6e5a5f6a8cf557b0688bc41dbb9dbc0bbd3a7

对于已经超过1字节的数字(如1000),则不需要在后面加上L。
数字后面也可以加上U,以说明它是一个无符号数。

2.2.3 有符号整数

上面说的这些数字都是无符号数。8位的内存单元可以存储的数字范围是0~255。但是在C语言中也需要负数。此时,用某个位表示符号位,0表示正数,1表示负数。在计算机中,负数使用补码来表示。之所以使用补码是为了使加减法运算都可以用一种电路实现。-2的补码是:
11111110
加1后等于:
11111111
这也是-1的补码,也就是我们期望的结果:-2 + 1 = -1。如果再加1就等于:
00000000
符合我们的期望。
再回想一下前面的里程表例子,初始全是0,如果想要表示-1,就需要向后拨1,这时里程表上就全是1了。
在C语言中,用下面的方法表示有符号数:


7a75172677023b9b4c0ae6ae322931b235d976b9

2.2.4 十六进制

十六进制使用16个符号计数:0~9以及A~F。这次可以设想下我们的里程表上每位有16种符号。使用十六进制数可以很好地帮助程序员理解每一位的值;如果使用二进制,就会显得很长,不方便记忆和交流。不像平常(在现实世界中)使用的十进制那样,十六进制和二进制之间可以直接换算。每个十六进制位占用4个比特位。十六进制的每个数字都可以用一个4比特的二进制数表示(见图2-4)。许多程序员都能记住每个十六进制数字对应的二进制数,这样他们就可以直观地看到十六进制数对应的二进制形式了。例如,期望传送过来的数字是AA(0b1010 1010),但是得到的却是55(0b0101 0101),我们立即就知道数据在传输中错乱了一位。


f594f91c709e1bec9474f7953c6b098142af27c6

图2-4 十六进制转换图
在本书中,十六进制数有时被用来描述内存中的地址,有时被用来表示数据。使用十六进制并不难,只需要多加练习即可。
一个字节可以用两位十六进制数表示。十六进制数通常排成两位或四位一组,左边是最高位。
本书中使用0x来表示十六进制数。最近也有些微芯片文献中使用“h”来表示十六进制数(但在C语言中不合法)。
图2-5给出了一些十六进制、二进制和十进制之间的换算结果。


3649f70e29319f17830af6a87e536557b5a76922

图2-5 数制转换示例
显然,0xFFFF(某些微控制器程序存储器的地址空间的最大值)比写成1111 1111 1111 1111 或者65535要简单得多。
在C语言中,十六进制数的前面需要加上0x。例如,给x赋值26(十进制)或者1A(十六进制):


05d6081aa40bb5a9e2423a51e888a7733d1aef49

2.2.5 八进制

八进制的基是8个分别代表不同数值的数字(0~7)。和十六进制一样,八进制也可以直接换算成二进制。每个八进制位占3个比特位。在十六进制流行之前,早期的计算机使用的是八进制,因为可以直接使用开关输入指令(见图2-6)。虽然已经很少有人再用八进制了,但是C语言仍就将其纳入自己的语法中。
在C语言中,数字前加上0表示该数字是一个八进制数。例如,给x赋值10(十进制)或者12(八进制):


77ed34af91c83c654dfffc82b7f1c4cc42fa726f

注意,等号后面的是数字0而不是字母O。即使你不打算用八进制,这里也要特别注意,在十进制的数字前加上0就变成了八进制。上面那行代码中,x是10,而不是12。


eaee43d23e4ed1929c6a9f3dd6cfad342b57e638

图2-6 DEC PDP-8的前面板
注: DEC PDP的控制杆用来输入二进制机器指令,然后使用load按钮将指令加载到内存中。注意,每3个控制杆为一组,被分成不同颜色。每组对应一个八进制数。这种计算机在C语言出现之前就已经非常流行。(PDP-8的图片由Herb Johnson提供,http://www.retrotechnology.com

2.2.6 浮点数

上面讲到的都是整数,没有小数点。浮点数指的是有小数点且小数点可以在很大范围内移动的数字。12.34就是一个典型的浮点数,在计算机内部用1234×10-2来存储它。这两部分分别称为尾数和指数,编译器规定了这两部分的范围。在小型PIC设备中,CCS C编译器使用24位尾数和6位指数,再加上尾数和指数的两个符号位,一共32位。也就是说下列数字可以表示成:
1.23 123×10-2
0.000?000?000?000?123 123×10-15
12?300?000?000?000?000 123×1014
但是数字:
1?200?000?000?000?003 1?200?000?000?000?003×100
只能转换成:
1?200?000?000?000?000 120×1014
因为尾数部分没有那么长来容纳这么多有效位。
由于浮点数在计算机中其实是一种不精确的表示,存在舍入误差,因此在比较两个浮点数时,不能使用小于和大于,而只能用等于和不等于。
在C语言中,浮点数有三种表示方法:


798b2811ddb60264f27f595787762bed9e5bcb20

2.2.7 定点数

定点数的小数点是固定的。在计算机中不需要存储小数点的位置,不需要区分尾数和指数,只需要像整数一样存储,编译器和处理器会使用约定的小数点位置来处理定点数。这种计算机结构简单,造价低廉。在程序中这样表示:


8407b100d5e8e605a74eada3c723fa939d461b80

2.2.8 字符

编程中的字符包括数字(0~9)和字母(A~Z)以及标点符号和特殊符号。C语言中,每个字符都可以对应到0~255中的一个数字。而ASCII码就是一套用8比特位来表示字符的标准编码。
ASCII表中一共有256个字符,附录A给出了完整的ASCII表。从表中可以看出,字母A在内存中是65。C语言中的字符在代码中需要用一对单引号括起来。由于C语言在类型转换时比较宽松(类型转换,同一个字符在编译器中可以有不同的解释),因此在代码中可以使用65来代替‘A’。字符赋值示例:


08dc6cebb6b21bf89c67269ae565b4299d798316

有些字符在键盘上找不到,可以用“转义”来表示它们。反斜线用来表示转义字符。如果想要使用反斜线,必须连续输入两个反斜线:\。如果要使用十六进制数表示字符,也要用到“转义”:'x41'(也就是字母'A'),用八进制表示就是'101'。表2-1列出了一些特殊字符。
表2-1 C语言转义字符
n 换行——x0a v 垂直制表——x0b
r 返回——x0d ? 问号——x3f
t 制表——x09 ’ 单引号——x22
b 退格——x08 ” 双引号——x22
f 换页——x0c \ 反斜杠——x5c
a 响铃——x07

注意,在使用反斜线时前面必须再加上一个反斜线表示这是个转义字符,有时还需要使用单引号或双引号。例如,下面这几行代码的功能是一样的,都是给变量c赋值一个换行符。


c925df5a3aee1b3dbbea7e182e2533d418a83e35

2.2.9 字符串

将一组字符连续存储在内存中,并以空字符'000'结尾,就构成了C语言中的字符串。例如:
“ABCD” 在内存中的内容 image

我们使用双引号来定义字符串。上面的字符串中,一共有4个字符,但是在内存中占用5个字符(最后一个0表示字符串结尾)。因此,该字符串需要5字节的内存。
字符串中也可以包含特殊字符,例如:
"A0x42111D" 相应内存中的内容 image

上面的字符串也占用5个字节,其中字母B和C分别用十六进制和八进制表示。在C字符串中经常会用到r(回车)和n(换行)。Windows文件系统在文件中使用这两个字符表示回车换行。下面的字符串表示文件中或者大多数程序终端显示的一行:
"Line Onern" 相应内存中的内容
还有个有意思的特性,如果代码中两个字符串之间仅存在空白字符,那么它们将被当作一个大的字符串处理。例如:
"ABCD" "EFGH"image

会被当作:
"ABCDEFGH" 在内存中的内容 image

注意,这里丢掉了D后面的0,因为两个小字符串被当作一个大字符串处理,在内存中占9个字节。
当需要将一个很长的字符串分开写到多行中时,以及在稍后章节中将介绍的宏,都会用到这个特性。

2.2.10 真和假

每个C语言表达式都可以得出一个数值。使用关系运算符的表达式的值可能是TRUE(1,真)或FALSE(0,假)。关系表达式通常用在if或while语句中:


5ebd97f411011bdd821a29c9b5f442b64e3a13f8

如果表达式的结果为TRUE,则会执行if之后的语句。如果表达式的结果为FALSE,则不会执行if后面的语句。关系表达式a

2.2.11 常量

前面已经用过这种简单的数据声明:


5ba917da2f4b02d3ce301b1ff85ddd2ba74f1bc5

还可以在它前面加上关键字const来声明一个符号常量:


ebd50b2f2da7b4e939b3f175b14a24e71832370c

定义好符号常量之后,在程序中可以直接使用常量名来代替该常量。还有一种更常用的定义常量的方法:


af81b70e76915c2c33ea8414616e1846af85c01f

这个预编译指令在编译前会将所有的LEVEL替换成10。下一章中会详细介绍预编译指令。
使用const的好处在于可以指定变量类型(如上面的int)。通常,程序员使用const定义符号常量时需要将变量名全大写。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:

华章出版社

官方博客
官网链接