计算机底层只存储数字,字符本质上也是用整数来表示的——ASCII 就是这套字符与数字之间的映射规则。
2.2
ASCII(美国信息交换标准代码)是一种将字符映射为数字的编码系统,最初于 1963 年发布。标准 ASCII 定义了 128 个字符(0~127),包括控制字符和可显示字符。计算机底层只存储数字,字符本质上也是用整数来表示的。
计算机的存储单元里只有 0 和 1,它只会存数字,不认识"字符"这种东西。那我们平时打的字母、标点、汉字是怎么显示出来的呢?答案是:人和计算机之间需要一份"翻译词典"——大家提前约定好,数字 65 就代表字母 'A',数字 97 就代表 'a'。计算机内部只存这个数字,显示的时候再按词典"翻译"成人能看懂的字符。
这份词典就是 ASCII 编码表。它把 128 个常用字符(英文字母、数字符号、标点、常见控制符)分别对应到 0~127 这 128 个整数上,成为几乎所有编程语言和计算机系统的共同基础。
char 类型本质上就是一个 1 字节的整数。写 char c = 'A'; 时,编译器实际存进去的是数字 65,只不过 char 在输出(如 cout)时会自动按 ASCII 表"翻译"成对应的字符显示出来。这也是为什么 char 可以像整数一样参与加减运算——它本来就是整数。与其死记硬背 128 个数字,不如先记住 ASCII 表被分成了几大块,各块内部又是连续排列的——这比记住某个字符具体是第几号更有用:
| 范围 | 类别 | 说明 |
|---|---|---|
| 0 ~ 31 | 控制字符 | 不可打印,用来控制设备行为,如换行 '\n'(10)、制表符 '\t'(9)、回车 '\r'(13) |
| 32 | 空格 | 唯一"看不见但占位置"的可打印字符,ASCII 表里最小的可打印字符 |
| 33 ~ 47 | 标点符号 | ! " # $ % & ' ( ) * + , - . / |
| 48 ~ 57 | 数字字符 | '0' ~ '9',注意这是"字符 0",不是数值 0,两者相差 48 |
| 58 ~ 64 | 标点符号 | : ; < = > ? @ |
| 65 ~ 90 | 大写字母 | 'A' ~ 'Z',连续排列,'A' 最小、'Z' 最大 |
| 91 ~ 96 | 标点符号 | [ \ ] ^ _ ` |
| 97 ~ 122 | 小写字母 | 'a' ~ 'z',与对应大写字母恰好相差 32 |
| 123 ~ 126 | 标点符号 | { | } ~ |
| 127 | DEL | 删除控制符,不可打印 |
'0' = 48,'A' = 65,'a' = 97——剩下的字符都可以通过"连续排列"和"相差 32"这两条规律直接推算出来,完全不需要死记整张表。正因为大写字母和小写字母在 ASCII 表里各自连续排列,且首字母恰好相差 32,数字字符 '0'~'9' 也连续排列,我们可以用简单的加减法在字符和数值之间转换,而不需要查表:
| 1 | // 技巧 1:大小写互转(差值为 32) |
| 2 | char lower = char('A' + 32); // 'a'(大写转小写) |
| 3 | char upper = char('a' - 32); // 'A'(小写转大写) |
| 4 | |
| 5 | // 技巧 2:数字字符 → 整数 |
| 6 | int n = '5' - '0'; // n = 5(字符减 '0' 得数值) |
| 7 | |
| 8 | // 技巧 3:整数 → 数字字符 |
| 9 | char ch = char(5 + '0'); // ch = '5'(数值加 '0' 得字符) |
以技巧 1 的大写转小写为例,把 'A' + 32 这一步拆开来看,实际发生的是"字符 → 数字 → 数字 → 字符"的过程:
技巧 2、3 的道理完全一样:字符 '5' 本质是数字 53,减去字符 '0'(数字 48),得到 53 - 48 = 5,这才是我们真正想要的数值 5——而不是字符 '5' 本身的编码值。
手动加减 32 或 48 虽然直观,但每次使用前都要先确认"这确实是个字母/数字",否则会算出无意义的结果。C++ 标准库 <cctype> 提供了一整套现成的字符判断和转换函数,更安全、更不容易出错,正式写代码时更推荐使用:
| 1 | #include <cctype> |
| 2 | |
| 3 | isalpha(ch) // 是否为字母(大写或小写) |
| 4 | isdigit(ch) // 是否为数字字符 '0'~'9' |
| 5 | isupper(ch) // 是否为大写字母 |
| 6 | islower(ch) // 是否为小写字母 |
| 7 | isspace(ch) // 是否为空白字符(空格/换行/制表符等) |
| 8 | toupper(ch) // 返回转换成大写后的字符 |
| 9 | tolower(ch) // 返回转换成小写后的字符 |
用库函数改写技巧 1 的大小写转换,不需要再关心背后的数字 32,也不用担心传入的字符本来就不是字母:
| 1 | char c = 'A'; |
| 2 | |
| 3 | if (isalpha(c)) { // 先判断确实是字母,再转换 |
| 4 | char lower = char(tolower(c)); // 'a' |
| 5 | } |
<cctype> 的判断函数打底,更不容易因为边界情况写出 bug。把本节内容汇总成几条最容易踩坑的规则:
'0' 的 ASCII 值是 48,不是 0。如果直接把字符当数值用(比如 int n = '5';),得到的是 53 而不是 5,一定要记得减去 '0'。isalpha、isdigit 先判断更保险。char 默认是有符号类型,取值范围是 -128~127。如果用它存储超出 127 的原始字节数据(比如图像像素值 0~255),可能会变成负数,导致比较或输出结果出乎意料。这种情况建议改用 unsigned char。