3.1 简单变量
当我们编写程序时,通常需要存储一些信息,这些信息有的被临时存储在内存中,有的则长期保存在硬盘等存储设备中。在程序运行期间,变量主要被存储在内存1中,本章我们将主要讨论存储在内存中的数据(即变量),的相关内容。把数据存储在计算机中,程序必须明白 3 个必要的特征:
- 数据将被存储在哪里
- 要以何种类型存储数据
- 要存储什么值(内容)
通过之前的“程序代码”范例,大家应该可以看到我们使用的声明中,类型描述了信息的类型并且通过特殊的字母组合(即单词)描述了其值的变量名称。例如程序代码2-2中使用了
int age;
age = 18;
这些语句告诉程序,他正在存储 int
型整数,并使用 age 来表示 该整数的值(这里是 18 )。实际上这里没有给出注释,但大家都可以明白 age 代表年龄并且年龄是 18,这便是本章下一小节即将提到的一个优秀的变量名的重要性。
程序会找到一块能够存储 int
型整数的内容,将所占用的内存(存储)单元标记为 age,并将 18 复制到该内存单元中。这些语句执行后,我们任何人不会知道这个值被存储在内存的什么位置,但我们的程序替我们记录了存储的信息。我们可以使用 &
操作符进行检索 age 在内存中的地址。在后续章节,我们会介绍指针的相关操作,届时我们会详细介绍这个操作符。
3.1.1 变量名
我们强烈建议
选择命名时要考虑代码的可读性和可维护性,尽量让其他开发者能够通过变量名快速理解其代表的含义。
前面我们说到,我们使用的 age 变量名,在没有任何提示的情况下,大多数人都可以明白 age 变量所代表的含义,这就是本小节我们要说的,C++ 提倡使用有一定含义的变量名。如果变量表示其他的含义,例如:灯光强度(Light intensity)应将其命名为 lightIntensity 或 light_intensity,尽量避免将其命名为 x 或 gq 等易造成混淆的名称。如果将其命名为 li 在特定的、不会引起混淆的代码环境中,可以使用简洁的缩写,但不太推荐在大型项目中单独使用这种极短的缩写,容易引起歧义。
C++ 必须遵守几个简单的命名规则
“C++ 必须遵守几个简单的命名规则”中最后一点与前几点有所不同。C++ 标准规定,以两个下划线开头且紧跟一个大写字母的名称(例如__A、__BIG_CONSTANT等)是保留给编译器及其使用的资源所使用的。这意味着我们不应该在自己的代码中使用这样的名称,否则可能会导致不可预测的结果,因为编译器可能在不同的阶段使用这些特定的名称来实现其内部的功能,或者与特定的平台相关的实现相关联。如果开发者使用了这些保留名称,可能会与编译器的行为产生冲突,导致编译错误、链接错误或者程序在运行时出现奇怪的行为。
以一个下划线开头的名称(例如_myVariable、_globalFunction等)在 C++ 中也有特殊的含义。它们被保留给实现,通常用于全局标识符。这意味着在全局作用域中,我们应该避免使用以单个下划线开头的名称作为自己定义的变量、函数或其他标识符的名称。这样的保留可以确保编译器和链接器在处理程序时不会与开发者定义的名称产生冲突,同时也为实现特定的平台相关功能或编译器内部的实现提供了一定的灵活性。
总之,如果使用我们将不知道会发生什么,但遵循这些命名规则可以避免与编译器和实现相关的名称冲突,提高程序的可移植性和稳定性。
下面是一些 C++ 中有效和无效的名称:
有效的: - myVariable:由字母开头,后续是字母、数字和下划线的组合,是典型的有效变量名; - _myVariable1:以单个下划线开头,是有效的,但一般不建议这么用,因为它被保留给实现(虽然通常情况下使用也不会有大问题); - myFunction:有效的函数名; - MyClass:有效的类名; - inputStream:有明确意义的名称,很常见且有效。
无效的:
- 1myVariable:以数字开头是无效的;
- my Variable:包含空格是无效的;
- my@Variable:包含特殊字符 @ 是无效的;
- int:这是 C++ 的关键字,不能作为标识符名称;
- class:同样是关键字,不能用作名称。
如果想使用两个及以上单词组成一个名称,通常的做法是用下划线将单词分开,例如:my_variable 或者从第二个单词开始将每个单词的第一个首字母大写(即小驼峰命名法),例如:myVariable。C 和 C++ 开发者更倾向于按照 C 语言的风格使用下划线,而 Java 程序员可能更偏向于使用小驼峰命名法。这两种形式都很容易将单词区分开。
tishi
在 C++ 中,对于类名一般使用大驼峰命名法(即每个单词首字母都大写,如 MyClass)。
此外,还有一些常见的命名约定,比如:
- 常量通常使用全大写字母,单词之间用下划线分隔,例如MAX_VALUE。
- 命名空间通常采用小写字母且可能使用下划线来分隔单词,如my_namespace。
总的来说,虽然没有严格的规定,但遵循一定的命名约定可以提高代码的可读性和可维护性。
3.1.2 整型
我们都知道,整数有无数个,但如果将无限大的整数也包括进来(尽管它确实属于整数)计算机就不可能用有限的内存来表示所有的整数。因此,我们只能表示所有整数的一个子集。C++ 提供多个整数类型,以便能根据具体要求选择使用最合适的整数类型。
C++ 基本整数类型分别有以下几种:
- char:
- 通常是 1 个字节大小,可以表示字符,也可以作为小范围的整数类型使用。有符号的
char
类型范围通常是 - 128 到 127,无符号的char
类型范围是 0 到 255。
- 通常是 1 个字节大小,可以表示字符,也可以作为小范围的整数类型使用。有符号的
- short(短整型):
- 一般为 2 个字节大小,具体大小和范围取决于编译器和平台。有符号短整型的范围通常在 - 32768 到 32767 之间。
- int(整型):
- 通常是 4 个字节大小。有符号整数范围通常在 - 2147483648 到 2147483647 之间。是最常用的整数类型之一。
- long(长整型):
- 在不同的平台上大小可能不同,通常至少为 4 个字节。有符号长整型的范围通常比有符号整型更大。
- long long(长长整型)4:
- C++11 新增的类型,通常为 8 个字节大小,提供更大范围的整数表示。有符号长长整型的范围通常非常大。
位与字节的概念
在计算机中,位(bit)和字节(byte)是重要的存储单位概念。
位(bit)是二进制数据中的最小单位,它的值只有 0 和 1 两种状态。每一位代表一个二进制数字。计算机中的所有数据都是以二进制形式存储和处理的,位用于表示单个的二进制数字,多个位组合在一起可以表示不同的数值、字符或指令。
例如:
- 在一个 8 位的二进制数中,每一位都可以是 0 或 1,通过不同的位组合可以表示 256 种不同的数值。
字节(byte)是计算机存储容量的基本单位。1 字节等于 8 位。通常用大写的“B”表示字节。字节用于衡量计算机存储容量和数据传输量。在存储方面,一个字符通常占用一个字节的存储空间。例如,在 ASCII 编码中,一个英文字母、数字或标点符号占用 1 个字节。而在 Unicode 编码中,一个字符可能占用多个字节。在数据传输方面,网络带宽通常以字节每秒(B/s)为单位来衡量数据传输的速度。
例如:
- 一个整数类型的数据在计算机中可能占用多个字节的存储空间。比如在 32 位系统中,一个
int
类型通常占用 4 个字节,也就是 32 位。 - 一个文件的大小通常以字节为单位来表示。例如,一个 10KB 的文件,实际上是 10 * 1024 = 10240 字节。
每种整型类型都有有符号和无符号版,有符号类型可以表示正数、负数和零。例如,有符号 int
类型通常的取值范围是 -2147483648 到 2147483647。存储时,最高位(最左边的位)被用作符号位,0 表示正数,1 表示负数。无符号类型只能表示非负整数,即零和正数。例如,无符号 int
类型的取值范围通常是 0 到 4294967295。所有的位都用于存储数值,没有符号位。
有符号类型在发生溢出时,行为通常是未定义的,但在一些情况下可能会以循环的方式处理。例如,有符号 char
类型,如果从最大值继续增加,可能会变为最小值。无符号类型在溢出时,会以循环的方式进行处理。例如,无符号 char
类型从最大值继续增加会变为 0,然后继续增加。有符号类型在进行比较时,会考虑符号。例如,-1 小于 0。无符号类型在比较时只考虑数值大小。如果将一个有符号负数与无符号数比较,有符号数会被隐式转换为无符号数,可能会导致意外的结果。例如,有符号的 -1 转换为无符号数时会变成一个非常大的正数,通常比任何无符号正数都大。
有符号类型在大多数常规的算术运算和需要考虑正负情况的场景中使用,例如,表示温度的变化、计数的增减等。 无符号类型在确定数值一定是非负的时候使用,例如,表示对象的数量、数组的索引等。而在一些特定的算法中,需要利用无符号类型的循环溢出特性来实现特定的逻辑。
截至目前 C++ 共有 10 种整型类型使用。
可以像使用 int
一样使用这些类型来声明变量:
short age //有符号短整型
int temperature //有符号整型
long number_of_rows //有符号长整型
long long coordinate_values //有符号长长整型
unsigned short age //无符号短整型
unsigned int temperature //无符号整型
unsigned long population //无符号长整型
unsigned long long number_of_particles //无符号长长整型
当一个数值超过了其对应数据类型的取值范围时,无符号类型变量的值会从 0 重新开始计数。例如,假设 unsigned int
的取值范围是 0 到 4294967295,如果一个无符号整数变量的值为 4294967295,再对其加 1,它会变为 0。当有符号整数类型变量在进行算术运算(如加法、减法等)时发生溢出,结果是未定义行为。在实际运行中,可能出现奇怪的值,或者程序可能出现错误行为甚至崩溃,具体结果取决于编译器和运行环境。例如,对于有符号 8 位整数类型 char
,如果其值为 127,再对其进行加 1 操作,结果是不确定的,可能是 -128(在一些编译器中会出现这种结果,因为最高位变为符号位,导致数值变为负数且取了最小值)。
sizeof 运算符
在 C 和 C++ 中,sizeof
是一个运算符,用于确定给定类型或变量在内存中占用的字节数
-
对于数据类型:
sizeof(type)
,其中 type 可以是任何内置数据类型(如 int、char、double 等)、用户自定义类型(如结构体、类等)或数组类型。 例如,sizeof(int)
返回 int 类型在当前环境下占用的字节数。 -
对于变量:
sizeof(var)
,其中 var 是一个已定义的变量。 例如,int a = 10; sizeof(a) 返回变量 a 所占用的字节数,在这种情况下与 sizeof(int) 的结果相同,因为变量 a 的类型是 int。
sizeof
返回的值是无符号整数类型,通常是 size_t
。
对于数组,sizeof
返回整个数组占用的字节数,而不是数组元素的个数。要确定数组元素的个数,可以使用 sizeof(array) / sizeof(array[0])
。
sizeof
在编译时就会被计算出来,不会导致运行时的性能开销。
3.1.3 类型的选择
通过上述学习,我们了解到 C++ 提供了大量的数据类型,应使用哪种类型最为合适呢?
通常,int
类型被设置为最“自然”的长度。自然长度(natural size)可以理解为该数据类型在当前编译器、操作系统和硬件平台组合下通常被分配的字节数。它可能是当前编译器、操作系统和硬件平台组合下处理效率最高的长度。如果没有特定需求或强有力的理由去选择其他类型,则我们建议使用 int
。
3.1.4 char 类型
char
类型,顾名思义,是为了专门存储字符(字符和数字)而设计的。计算机对于存储数字来说算不了什么,但存储字母则情况不同。编程语言通过使用字母的数值编码解决了这个复杂的问题。因此,char
类型可以说是另外一种整形。它足够长,能够表示目标计算机系统中的所有基本符号(数字、字母、符号等)。实际上,大多系统支持的字符都不超过 256 种,因此一个字节就可以表示所有的符号。虽然 char
通常被用来处理字符,但你也可以将它用作比 short
更小的整形。
通常我们最常用的符号集是美国信息交换标准代码(ASCII, American Standard Code for Information Interchange)字符集。
ASCII码表 (点击展开)
Bin(二进制) | Dec(十进制) | Hex(十六进制) | 缩写/字符 | 解释 |
---|---|---|---|---|
0000 0000 | 0 | 0x00 | NUL(null) | 空字符 |
0000 0001 | 1 | 0x01 | SOH(start of headline) | 标题开始 |
0000 0010 | 2 | 0x02 | STX (start of text) | 正文开始 |
0000 0011 | 3 | 0x03 | ETX (end of text) | 正文结束 |
0000 0100 | 4 | 0x04 | EOT (end of transmission) | 传输结束 |
0000 0101 | 5 | 0x05 | ENQ (enquiry) | 请求 |
0000 0110 | 6 | 0x06 | ACK (acknowledge) | 收到通知 |
0000 0111 | 7 | 0x07 | BEL (bell) | 响铃 |
0000 1000 | 8 | 0x08 | BS (backspace) | 退格 |
0000 1001 | 9 | 0x09 | HT (horizontal tab) | 水平制表符 |
0000 1010 | 10 | 0x0A | LF (NL line feed, new line) | 换行键 |
0000 1011 | 11 | 0x0B | VT (vertical tab) | 垂直制表符 |
0000 1100 | 12 | 0x0C | FF (NP form feed, new page) | 换页键 |
0000 1101 | 13 | 0x0D | CR (carriage return) | 回车键 |
0000 1110 | 14 | 0x0E | SO (shift out) | 不用切换 |
0000 1111 | 15 | 0x0F | SI (shift in) | 启用切换 |
0001 0000 | 16 | 0x10 | DLE (data link escape) | 数据链路转义 |
0001 0001 | 17 | 0x11 | DC1 (device control 1) | 设备控制1 |
0001 0010 | 18 | 0x12 | DC2 (device control 2) | 设备控制2 |
0001 0011 | 19 | 0x13 | DC3 (device control 3) | 设备控制3 |
0001 0100 | 20 | 0x14 | DC4 (device control 4) | 设备控制4 |
0001 0101 | 21 | 0x15 | NAK (negative acknowledge) | 拒绝接收 |
0001 0110 | 22 | 0x16 | SYN (synchronous idle) | 同步空闲 |
0001 0111 | 23 | 0x17 | ETB (end of trans. block) | 结束传输块 |
0001 1000 | 24 | 0x18 | CAN (cancel) | 取消 |
0001 1001 | 25 | 0x19 | EM (end of medium) | 媒介结束 |
0001 1010 | 26 | 0x1A | SUB (substitute) | 代替 |
0001 1011 | 27 | 0x1B | ESC (escape) | 换码(溢出) |
0001 1100 | 28 | 0x1C | FS (file separator) | 文件分隔符 |
0001 1101 | 29 | 0x1D | GS (group separator) | 分组符 |
0001 1110 | 30 | 0x1E | RS (record separator) | 记录分隔符 |
0001 1111 | 31 | 0x1F | US (unit separator) | 单元分隔符 |
0010 0000 | 32 | 0x20 | (space) | 空格 |
0010 0001 | 33 | 0x21 | ! | 叹号 |
0010 0010 | 34 | 0x22 | " | 双引号 |
0010 0011 | 35 | 0x23 | # | 井号 |
0010 0100 | 36 | 0x24 | $ | 美元符 |
0010 0101 | 37 | 0x25 | % | 百分号 |
0010 0110 | 38 | 0x26 | & | 和号 |
0010 0111 | 39 | 0x27 | ' | 单引号 |
0010 1000 | 40 | 0x28 | ( | 开括号 |
0010 1001 | 41 | 0x29 | ) | 闭括号 |
0010 1010 | 42 | 0x2A | * | 星号 |
0010 1011 | 43 | 0x2B | + | 加号 |
0010 1100 | 44 | 0x2C | , | 逗号 |
0010 1101 | 45 | 0x2D | - | 减号/破折号 |
0010 1110 | 46 | 0x2E | . | 句号 |
0010 1111 | 47 | 0x2F | / | 斜杠 |
0011 0000 | 48 | 0x30 | 0 | 字符0 |
0011 0001 | 49 | 0x31 | 1 | 字符1 |
0011 0010 | 50 | 0x32 | 2 | 字符2 |
0011 0011 | 51 | 0x33 | 3 | 字符3 |
0011 0100 | 52 | 0x34 | 4 | 字符4 |
0011 0101 | 53 | 0x35 | 5 | 字符5 |
0011 0110 | 54 | 0x36 | 6 | 字符6 |
0011 0111 | 55 | 0x37 | 7 | 字符7 |
0011 1000 | 56 | 0x38 | 8 | 字符8 |
0011 1001 | 57 | 0x39 | 9 | 字符9 |
0011 1010 | 58 | 0x3A | : | 冒号 |
0011 1011 | 59 | 0x3B | ; | 分号 |
0011 1100 | 60 | 0x3C | < | 小于 |
0011 1101 | 61 | 0x3D | = | 等号 |
0011 1110 | 62 | 0x3E | > | 大于 |
0011 1111 | 63 | 0x3F | ? | 问号 |
0100 0000 | 64 | 0x40 | @ | 电子邮件符号 |
0100 0001 | 65 | 0x41 | A | 大写字母 A |
0100 0010 | 66 | 0x42 | B | 大写字母 B |
0100 0011 | 67 | 0x43 | C | 大写字母 C |
0100 0100 | 68 | 0x44 | D | 大写字母 D |
0100 0101 | 69 | 0x45 | E | 大写字母 E |
0100 0110 | 70 | 0x46 | F | 大写字母 F |
0100 0111 | 71 | 0x47 | G | 大写字母 G |
0100 1000 | 72 | 0x48 | H | 大写字母 H |
0100 1001 | 73 | 0x49 | I | 大写字母 I |
01001010 | 74 | 0x4A | J | 大写字母 J |
0100 1011 | 75 | 0x4B | K | 大写字母 K |
0100 1100 | 76 | 0x4C | L | 大写字母 L |
0100 1101 | 77 | 0x4D | M | 大写字母 M |
0100 1110 | 78 | 0x4E | N | 大写字母 N |
0100 1111 | 79 | 0x4F | O | 大写字母 O |
0101 0000 | 80 | 0x50 | P | 大写字母 P |
0101 0001 | 81 | 0x51 | Q | 大写字母 Q |
0101 0010 | 82 | 0x52 | R | 大写字母 R |
0101 0011 | 83 | 0x53 | S | 大写字母 S |
0101 0100 | 84 | 0x54 | T | 大写字母 T |
0101 0101 | 85 | 0x55 | U | 大写字母 U |
0101 0110 | 86 | 0x56 | V | 大写字母 V |
0101 0111 | 87 | 0x57 | W | 大写字母 W |
0101 1000 | 88 | 0x58 | X | 大写字母 X |
0101 1001 | 89 | 0x59 | Y | 大写字母 Y |
0101 1010 | 90 | 0x5A | Z | 大写字母 Z |
0101 1011 | 91 | 0x5B | [ | 开方括号 |
0101 1100 | 92 | 0x5C | |反斜杠 | |
0101 1101 | 93 | 0x5D | ] | 闭方括号 |
0101 1110 | 94 | 0x5E | ^ | 脱字符 |
0101 1111 | 95 | 0x5F | _ | 下划线 |
0110 0000 | 96 | 0x60 | ` | 开单引号 |
0110 0001 | 97 | 0x61 | a | 小写字母 a |
0110 0010 | 98 | 0x62 | b | 小写字母 b |
0110 0011 | 99 | 0x63 | c | 小写字母 c |
0110 0100 | 100 | 0x64 | d | 小写字母 d |
0110 0101 | 101 | 0x65 | e | 小写字母 e |
0110 0110 | 102 | 0x66 | f | 小写字母 f |
0110 0111 | 103 | 0x67 | g | 小写字母 g |
0110 1000 | 104 | 0x68 | h | 小写字母 h |
0110 1001 | 105 | 0x69 | i | 小写字母 i |
0110 1010 | 106 | 0x6A | j | 小写字母 j |
0110 1011 | 107 | 0x6B | k | 小写字母 k |
0110 1100 | 108 | 0x6C | l | 小写字母 l |
0110 1101 | 109 | 0x6D | m | 小写字母 m |
0110 1110 | 110 | 0x6E | n | 小写字母 n |
0110 1111 | 111 | 0x6F | o | 小写字母 o |
0111 0000 | 112 | 0x70 | p | 小写字母 p |
0111 0001 | 113 | 0x71 | q | 小写字母 q |
0111 0010 | 114 | 0x72 | r | 小写字母 r |
0111 0011 | 115 | 0x73 | s | 小写字母 s |
0111 0100 | 116 | 0x74 | t | 小写字母 t |
0111 0101 | 117 | 0x75 | u | 小写字母 u |
0111 0110 | 118 | 0x76 | v | 小写字母 v |
0111 0111 | 119 | 0x77 | w | 小写字母 w |
0111 1000 | 120 | 0x78 | x | 小写字母 x |
0111 1001 | 121 | 0x79 | y | 小写字母 y |
0111 1010 | 122 | 0x7A | z | 小写字母 z |
0111 1011 | 123 | 0x7B | { | 开花括号 |
0111 1100 | 124 | 0x7C | | | 垂线 |
0111 1101 | 125 | 0x7D | } | 闭花括号 |
0111 1110 | 126 | 0x7E | ~ | 波浪号 |
0111 1111 | 127 | 0x7F | DEL (delete) | 删除 |
字符集中的字符用 ASCII 表中的十进制数值编码表示。例如字符 A 的编码为 65,字符 a 的编码为 97。为了方便起见,我们只会记忆码表的一部分规律和个别开头字符,例如:
- 数字 0 - 9 的 ASCII 码是连续的,可以一起记忆,编码范围是 48('0')到 57('9');
- 大写字母 A - Z 的 ASCII 码也是连续的,范围是 65('A')到 90('Z');
- 小写字母 a - z 的 ASCII 码同样连续,范围是 97('a')到 122('z')。
不过 ASCII 编码并不能很好的满足国际化需求,C++ 支持的宽字符类型可以存储更多的值。但考虑到大多数读者对于学习难易的接受程度不同,我们只能优先出于简单学习起见,介绍 ASCII 码(也许随着本书的完善,我们会在后续章节对其他编码作出介绍,例如支持中文和其他基本包含所有国际化文字的 Unicode 字符集)。
但我们会在本章后续介绍一种 C++ 支持的宽字符 wchar_t
类型。
#include <iostream>
using namespace std;
int main()
{
char character; //声明 char 类型的 character 变量
cout << "Please enter a character: ";
cin >> character; //输入一个字符
cout << character << endl; //程序输出输入的字符 第一次结果输出
int decCharacter = character; //声明 int 类型的 decCharacter 变量,用于存储字符的十进制值
int newCharacter = decCharacter + 1; //声明 int 类型的 newCharacter 变量,用于存储输入字符下一个字符的十进制值
char nextCharacter = newCharacter; //将新的结果存储(转换)为 char 类型的值
cout << "Input characters: " << character << endl;
cout << "Enter the decimal value of the character: " << decCharacter << endl;
cout << "The character that is the decimal value of the input character plus one: " << nextCharacter << endl;
return 0;
}
程序代码3-1使用了 char
类型,我们输入了某一个字符(这里假定我们输入字符 'E'),程序将会输出:
Please enter a character: E
E
Input characters: E
Enter the decimal value of the character: 69
The character that is the decimal value of the input character plus one: F
其运行结果很有趣,我们输入了一个字母,程序会输出该字母而不是对应的字符编码。反之同样,当你尝试输入字符编码后程序也会输出字符编码,而不是字母。这种有趣的现象源于 char
类型。而如果通过断点调试后你会发现,输入时 cin
将 E 转为了 69 存储在内存中,输出时又进行了逆操作。而 cin
和 cout
都是由变量的类型所引导的。如果将 69 存储在 int
变量中,cout
将会把他显示为 69。
在 C++ 中,我们将字符用单引号引起来,例如:char character = 'E'
,将字符串用双引号引起来,例如:string str = "Echo"
。cout
对象可以处理这两种情况。同时借此我们将会引导大家学习一项 cout
的新特性 —— cout.put()
函数。
3.1.4-1 成员函数 cout.put()
到底什么是 cout.put()
函数?其中为什么会需要用一个点来点出函数?
cout.put()
函数是一个重要的 C++ 面向对象(OOP)概念 —— 成员函数。
这部分我们会在后续章节类和对象中为大家详细讲解,这里只做点睛之笔。类定义了如何表示和控制数据,成员函数归类所有。例如类 ostream
中有一个叫做 put()
的函数,用于输出字符,但类只能通过特定对象(这里是 cout
对象)来使用成员函数。要通过对象 cout
来使用成员函数,就必须使用点来将对象名和成员函数所连接。这个点(更准确叫句点)被称为成员操作符。而 cout.put()
的意思是通过类对象 cout
访问(或使用)成员 put()
函数。如本段开头所说,我们会在后续章节详细介绍这一点,现在我们接触过的类只有 istream/ostream(iostream),现在你可以通过使用他们的成员函数来熟悉这一概念。
3.1.4-2 char 常量
在 C++ 中字符常量有很多种书写方式。对于常规字符(如字母、标点符号和数字),最简单的方法是将字符用单引号引起来。这种表示法代表的是字符的数值编码。例如 ASCII 系统中对应的情况:
- 'A' 为 65,即字符 A 的 ASCII 码。
- 'a' 为 97,即字符 a 的 ASCII 码。
- '1' 为 49,即数字 1 的 ASCII 码。
- '!' 为 33,即字符 ! 的 ASCII 码。
- ' ' 为 32,即空格的 ASCII 码。
这种表示方法优于数值编码,它更加清晰,且不需要知道编码方式。如果系统使用的是非 ASCII 编码,则 A 的编码将不是 65 ,但 'A' 表示的仍然是字符 A。
当然有些字符不能直接通过键盘输入到程序当中。例如:在程序中想要输出一个换行,我们并不能通过 Enter 键让字符串进行换行。因为编辑器会把这种操作视为编辑另起一行。再例如:我们不能把双印号用来分割字符串或者对一个词组打引号,因为 C++ 对这些字符提供了一种特殊的含义,第一个双引号代表字符串开始,第二个双引号代表字符串截止,如果字符串中间出现一个双引号,程序将会认为是两个字符串,且第二个字符串没有正常结尾。
但 C++ 提供了一种特殊的表示方法 —— 字符转义,如下表所示。例如:\n
表示换行符,\"
会将双引号作为常规的字符,而不是字符串分隔符。
字符名称 | ASCII符号 | C++代码 | 十进制ASCII码 | 十六进制ASCII码 |
---|---|---|---|---|
换行符 | LF(\n) | "\n" |
10 | 0x0A |
回车符 | CR(\r) | "\r" |
13 | 0x0D |
水平制表符 | HT(\t) | "\t" |
9 | 0x09 |
垂直制表符 | VT(\v) | "\v" |
11 | 0x0B |
退格符 | BS(\b) | "\b" |
8 | 0x08 |
换页符 | FF(\f) | "\f" |
12 | 0x0C |
响铃符 | BELL(\a) | "\a" |
7 | 0x07 |
反斜杠 | \ | "\\" |
92 | 0x5C |
单引号 | ' | "\'" |
39 | 0x27 |
双引号 | " | "\"" |
34 | 0x22 |
空字符 | NUL | "\0" |
0 | 0x00 |
下面来看看我们如何使用转义字符。
注意
我们应该像处理常规字符时一样来处理转义字符。也就是说,将他们视为字符常量时应该使用单引号引起来,将他们放在字符串中时,不要使用单引号。
char escapeChar = '\n';
cout << "Hello" << escapeChar << "World"<< \t <<"\"Here\" we are";
它的输出如下:
Hello
World "Here" we are
3.1.4-3 signed char 和 unsigned char
与 int
不同的是 char
在默认情况下既不是没有符号,也不是有符号的。是否有符号是由 C++ 实现决定的,这样编译器开发人员可以最大限度的与硬件属性所匹配。如果 char
有某种特定的行为,你则可以显示的将类型设置为 signed char
或 unsigned char
。
例如:当适用于需要表示有符号的小整数的情况,比如在处理一些小范围的温度变化值(可能有正有负)、误差值等场景时,可以使用 signed char
来存储温度的变化量(假设精度合适)。而当用于存储原始字节数据,比如图像、音频等文件的字节流数据。因为这些数据通常是无符号的字节序列,用 0~255 表示不同的颜色强度(在图像中)或者音频样本的幅度时,可以存储在 unsigned char
类型的变量中。
3.1.4-4 wcha_t
wchar_t
是 C++ 中的宽字符类型。它用于表示比普通字符(char
)更宽的字符编码,主要是为了处理 Unicode 字符或其他多字节字符集。在一些需要处理国际化文本(如包含多种语言字符)的程序中非常有用。例如,许多非英语语言有字符不能用单个字节(char
)来准确表示,像中文、日文、韩文等语言的字符,这时候就需要使用 wchar_t
来存储。
wchar_t
的大小不是固定的,它取决于编译器和操作系统。在大多数现代系统中,wchar_t
通常占用 2 个字节或者 4 个字节。例如,在 Windows 操作系统下,wchar_t
一般是 16 位(2 个字节),用于存储 UTF-16 编码的字符;而在一些 Unix/Linux 系统中,则可能是 32 位(4 个字节),用于存储 UTF-32 编码的字符。
它存储的是字符的编码值。例如,对于一个 Unicode 字符,其对应的编码会存储在 wchar_t
变量中。如果是 UTF-16 编码,每个 wchar_t
可以存储一个 16-bit 的 Unicode 码点;如果是 UTF-32 编码,每个 wchar_t
可以存储一个 32-bit 的 Unicode 码点。
要声明一个 wchar_t
类型的变量,可以像这样:wchar_t myWideChar;
。要定义一个宽字符常量,需要在字符前面加上L前缀。例如:wchar_t wc = L'A';
表示一个宽字符常量 A。如果要表示一个非英文字符,如汉字“中”,可以写成 wchar_t chineseChar = L'中';
(这里假设 wchar_t
的大小和编码方式能够正确表示这个汉字)。
而对于宽字符串,可以使用 wcslen
(计算宽字符串长度)、wcscpy
(复制宽字符串)等函数。这些函数和处理普通字符串(char
类型)的函数类似,但用于宽字符环境。例如:
#include <iostream>
#include <cwchar>
using namespace std;
int main()
{
wchar_t wstr[] = L"Hello, 世界";
int length = wcslen(wstr);
wcout << L"宽字符串长度为: " << length << std::endl;
return 0;
}
在程序代码3-2中,我们使用 wcslen
函数计算了包括非英文字符在内的宽字符串的长度,wcout
用于输出宽字符流,它类似于 cout
,但用于宽字符。
3.1.5 Bool 类型
这里我们只简单介绍 C++ 中布尔值的发展起源,至于使用,我们会在后续章节中多次且重点的使用到,届时我们将会向你着重强调用法及要点。
C++ 语言是在 C 语言的基础上发展而来的。在早期的 C 语言(C89 及以前版本)中没有布尔(bool
)类型。C 语言中通常使用整数类型(如int
)来表示逻辑值,其中 0 表示假(false),非零值表示真(true)。这种方式虽然能够实现逻辑判断,但在代码的可读性方面存在一定的不足。布尔类型随着 C++ 标准的发展而引入。从 C++98 标准开始,正式添加了 bool
类型。这使得程序员能够更清晰、更符合逻辑地处理布尔值相关的操作。
提示
任何数值和指针都可以被隐式转换为布尔值。任何非零值都被转换为 true,任何零值将会被转换为 false。
-
内存分为不同的区域,比如栈空间、堆空间等。在程序运行过程中,局部变量通常存储在栈上,动态分配的内存通常在堆上。当程序执行完毕,操作系统会回收程序所占用的内存资源,这些内存中的数据通常会被清除。也有一些特殊情况,比如某些操作系统可能会采用一些技术来缓存部分内存内容,或者某些恶意软件可能会尝试在程序结束后从内存中窃取信息,但从正常的程序设计和运行角度来说,程序结束后,内存中的绝大部分信息会被认为是丢失的。 ↩
-
C++ 是严格区分大小写的编程语言,所以不同大小写的标识符被视为不同的名称,例如:ABC 和 abc 将被认为是两个变量名称。 ↩
-
全局标识符是在程序的全局作用域中定义的名称。它可以是全局变量、全局函数、命名空间级别的实体等。 ↩
-
在 C++11 标准中,引入了 long long 这种新的整数数据类型。虽然一些编译器可能已经支持 long long 作为扩展类型,但它并不是 C++ 标准所规定的类型。C++11 标准将其纳入,使其成为合法的、被标准认可的类型。 ↩