《Hello C++》3.1 简单变量

当我们编写程序时,通常需要存储一些信息,这些信息有的被临时存储在内存中,有的则长期保存在硬盘等存储设备中。在程序运行期间,变量主要被存储在内存[1]中,本章我们将主要讨论存储在内存中的数据(即变量),的相关内容。把数据存储在计算机中,程序必须明白 3 个必要的特征:

  • 数据将被存储在哪里
  • 要以何种类型存储数据
  • 要存储什么值(内容)

通过之前的“程序代码”范例,大家应该可以看到我们使用的声明中,类型描述了信息的类型并且通过特殊的字母组合(即单词)描述了其值的变量名称。例如程序代码2-2中使用了

1
2
int age;
age = 18;

这些语句告诉程序,他正在存储 int 型整数,并使用 age 来表示 该整数的值(这里是 18 )。实际上这里没有给出注释,但大家都可以明白 age 代表年龄并且年龄是 18,这便是本文下一小节(3.1.1 变量名)即将提到的一个优秀的变量名的重要性。

程序会找到一块能够存储 int 型整数的内容,将所占用的内存(存储)单元标记为 age,并将 18 复制到该内存单元中。这些语句执行后,我们任何人不会知道这个值被存储在内存的什么位置,但我们的程序替我们记录了存储的信息。我们可以使用 & 操作符进行检索 age 在内存中的地址。在后续章节,我们会介绍指针的相关操作,届时我们会详细介绍这个操作符。

3.1.1 变量名

我们强烈建议,选择命名时要考虑代码的可读性和可维护性,尽量让其他开发者能够通过变量名快速理解其代表的含义。

前面我们说到,我们使用的 age 变量名,在没有任何提示的情况下,大多数人都可以明白 age 变量所代表的含义,这就是本小节我们要说的,C++ 提倡使用有一定含义的变量名。如果变量表示其他的含义,例如:灯光强度(Light intensity)应将其命名为 lightIntensity 或 light_intensity,尽量避免将其命名为 x 或 gq (光强)等易造成混淆的名称。如果将其命名为 li 在特定的、不会引起混淆的代码环境中,可以使用简洁的缩写,但不太推荐在大型项目中单独使用这种极短的缩写,这非常容易引起歧义。

C++ 必须遵守几个简单的命名规则

  • 在名称中智能使用字母、数字和下划线(_);
  • 名称中的第一个字符不能是数字;
  • 不能将 C++ 中的关键字用于变量名称;
  • C++ 严格区分大小写字符;
  • 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 程序员可能更偏向于使用小驼峰命名法。这两种形式都很容易将单词区分开。

在 C++ 中,对于类名一般使用大驼峰命名法(即每个单词首字母都大写,如 MyClass)。

此外,还有一些常见的命名约定,比如:

  • 常量通常使用全大写字母,单词之间用下划线分隔,例如 MAX_VALUE。
  • 命名空间通常采用小写字母且可能使用下划线来分隔单词,如 my_namespace。

总的来说,虽然没有严格的规定,但遵循一定的命名约定可以提高代码的可读性和可维护性。

3.1.2 整型

我们都知道,整数有无数个,但如果将无限大的整数也包括进来(尽管它确实属于整数)计算机就不可能用有限的内存来表示所有的整数。因此,我们只能表示所有整数的一个子集。C++ 提供多个整数类型,以便能根据具体要求选择使用最合适的整数类型。

C++ 基本整数类型分别有以下几种:

  1. char:
    通常是 1 个字节大小,可以表示字符,也可以作为小范围的整数类型使用。有符号的 char 类型范围通常是 - 128 到 127,无符号的 char 类型范围是 0 到 255。

  2. short(短整型):
    一般为 2 个字节大小,具体大小和范围取决于编译器和平台。有符号短整型的范围通常在 - 32768 到 32767 之间。

  3. int(整型):
    通常是 4 个字节大小。有符号整数范围通常在 - 2147483648 到 2147483647 之间。是最常用的整数类型之一。

  4. long(长整型):
    在不同的平台上大小可能不同,通常至少为 4 个字节。有符号长整型的范围通常比有符号整型更大。

  5. long long(长长整型)[2]
    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 转换为无符号数时会变成一个非常大的正数,通常比任何无符号正数都大。

有符号类型在大多数常规的算术运算和需要考虑正负情况的场景中使用,例如,表示温度的变化、计数的增减等。 无符号类型在确定数值一定是非负的时候使用,例如,表示对象的数量、数组的索引等。而在一些特定的算法中,需要利用无符号类型的循环溢出特性来实现特定的逻辑。

截至 C23 共有 10 种整型类型使用。但需注意 “10 种整型类型”是一个便于记忆的简化模型,它涵盖了最核心、最常用的整型类别。但 C 标准实际定义的整型类型体系要更精细和复杂。

可以像使用 int 一样使用这些类型来声明变量:

1
2
3
4
5
6
7
8
9
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 000000x00NUL(null)空字符
0000 000110x01SOH(start of headline)标题开始
0000 001020x02STX (start of text)正文开始
0000 001130x03ETX (end of text)正文结束
0000 010040x04EOT (end of transmission)传输结束
0000 010150x05ENQ (enquiry)请求
0000 011060x06ACK (acknowledge)收到通知
0000 011170x07BEL (bell)响铃
0000 100080x08BS (backspace)退格
0000 100190x09HT (horizontal tab)水平制表符
0000 1010100x0ALF (NL line feed, new line)换行键
0000 1011110x0BVT (vertical tab)垂直制表符
0000 1100120x0CFF (NP form feed, new page)换页键
0000 1101130x0DCR (carriage return)回车键
0000 1110140x0ESO (shift out)不用切换
0000 1111150x0FSI (shift in)启用切换
0001 0000160x10DLE (data link escape)数据链路转义
0001 0001170x11DC1 (device control 1)设备控制1
0001 0010180x12DC2 (device control 2)设备控制2
0001 0011190x13DC3 (device control 3)设备控制3
0001 0100200x14DC4 (device control 4)设备控制4
0001 0101210x15NAK (negative acknowledge)拒绝接收
0001 0110220x16SYN (synchronous idle)同步空闲
0001 0111230x17ETB (end of trans. block)结束传输块
0001 1000240x18CAN (cancel)取消
0001 1001250x19EM (end of medium)媒介结束
0001 1010260x1ASUB (substitute)代替
0001 1011270x1BESC (escape)换码(溢出)
0001 1100280x1CFS (file separator)文件分隔符
0001 1101290x1DGS (group separator)分组符
0001 1110300x1ERS (record separator)记录分隔符
0001 1111310x1FUS (unit separator)单元分隔符
0010 0000320x20(space)空格
0010 0001330x21!叹号
0010 0010340x22"双引号
0010 0011350x23#井号
0010 0100360x24$美元符
0010 0101370x25%百分号
0010 0110380x26&和号
0010 0111390x27单引号
0010 1000400x28(开括号
0010 1001410x29)闭括号
0010 1010420x2A*星号
0010 1011430x2B+加号
0010 1100440x2C,逗号
0010 1101450x2D-减号/破折号
0010 1110460x2E.句号
0010 1111470x2F/斜杠
0011 0000480x300字符0
0011 0001490x311字符1
0011 0010500x322字符2
0011 0011510x333字符3
0011 0100520x344字符4
0011 0101530x355字符5
0011 0110540x366字符6
0011 0111550x377字符7
0011 1000560x388字符8
0011 1001570x399字符9
0011 1010580x3A:冒号
0011 1011590x3B;分号
0011 1100600x3C<小于
0011 1101610x3D=等号
0011 1110620x3E>大于
0011 1111630x3F?问号
0100 0000640x40@电子邮件符号
0100 0001650x41A大写字母 A
0100 0010660x42B大写字母 B
0100 0011670x43C大写字母 C
0100 0100680x44D大写字母 D
0100 0101690x45E大写字母 E
0100 0110700x46F大写字母 F
0100 0111710x47G大写字母 G
0100 1000720x48H大写字母 H
0100 1001730x49I大写字母 I
01001010740x4AJ大写字母 J
0100 1011750x4BK大写字母 K
0100 1100760x4CL大写字母 L
0100 1101770x4DM大写字母 M
0100 1110780x4EN大写字母 N
0100 1111790x4FO大写字母 O
0101 0000800x50P大写字母 P
0101 0001810x51Q大写字母 Q
0101 0010820x52R大写字母 R
0101 0011830x53S大写字母 S
0101 0100840x54T大写字母 T
0101 0101850x55U大写字母 U
0101 0110860x56V大写字母 V
0101 0111870x57W大写字母 W
0101 1000880x58X大写字母 X
0101 1001890x59Y大写字母 Y
0101 1010900x5AZ大写字母 Z
0101 1011910x5B[开方括号
0101 1100920x5C\反斜杠
0101 1101930x5D]闭方括号
0101 1110940x5E^脱字符
0101 1111950x5F_下划线
0110 0000960x60`开单引号
0110 0001970x61a小写字母 a
0110 0010980x62b小写字母 b
0110 0011990x63c小写字母 c
0110 01001000x64d小写字母 d
0110 01011010x65e小写字母 e
0110 01101020x66f小写字母 f
0110 01111030x67g小写字母 g
0110 10001040x68h小写字母 h
0110 10011050x69i小写字母 i
0110 10101060x6Aj小写字母 j
0110 10111070x6Bk小写字母 k
0110 11001080x6Cl小写字母 l
0110 11011090x6Dm小写字母 m
0110 11101100x6En小写字母 n
0110 11111110x6Fo小写字母 o
0111 00001120x70p小写字母 p
0111 00011130x71q小写字母 q
0111 00101140x72r小写字母 r
0111 00111150x73s小写字母 s
0111 01001160x74t小写字母 t
0111 01011170x75u小写字母 u
0111 01101180x76v小写字母 v
0111 01111190x77w小写字母 w
0111 10001200x78x小写字母 x
0111 10011210x79y小写字母 y
0111 10101220x7Az小写字母 z
0111 10111230x7B{开花括号
0111 11001240x7C|垂线
0111 11011250x7D}闭花括号
0111 11101260x7E~波浪号
0111 11111270x7FDEL (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 字符集)。

但我们会在本文后续小节(3.1.4-4 wchar_t)介绍一种 C++ 支持的宽字符 wchar_t 类型。

程序代码3-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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’),程序将会输出:

1
2
3
4
5
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 成员函数[3] cout.put()

到底什么是 cout.put() 函数?其中为什么会需要“用一个点来点出”函数?

成员函数是一个重要的 C++ 面向对象(OOP)概念。它用于操作类的数据成员并定义对象的行为。

这部分我们会在后续章节类和对象中为大家详细讲解,这里只做点睛之笔(你可以点击 3.1.4-1 成员函数处的上标5,在脚注处我举了一个通俗易懂的成员函数的例子)。

类定义了如何表示和控制数据,成员函数归类所有。例如类 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"100x0A
回车符CR(\r)"\r"130x0D
水平制表符HT(\t)"\t"90x09
垂直制表符VT(\v)"\v"110x0B
退格符BS(\b)"\b"80x08
换页符FF(\f)"\f"120x0C
响铃符BELL(\a)"\a"70x07
反斜杠\"\\"920x5C
单引号"\'"390x27
双引号""\""340x22
空字符NUL"\0"00x00

下面来看看我们如何使用转义字符。

我们应该像处理常规字符时一样来处理转义字符。也就是说,将他们视为字符常量时应该使用单引号引起来,将他们放在字符串中时,不要使用单引号。

1
2
char escapeChar = '\n';
cout << "Hello" << escapeChar << "World"<< \t <<"\"Here\" we are";

它的输出如下:

1
2
Hello
World "Here" we are

3.1.4-3 signed char 和 unsigned char

与 int 不同的是 char 在默认情况下既不是没有符号,也不是有符号的。是否有符号是由 C++ 实现决定的,这样编译器开发人员可以最大限度的与硬件属性所匹配。如果 char 有某种特定的行为,你则可以显示的将类型设置为 signed charunsigned 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 类型)的函数类似,但用于宽字符环境。例如:

程序代码3-2
1
2
3
4
5
6
7
8
9
10
11
12
#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。


  1. 内存分为不同的区域,比如栈空间、堆空间等。在程序运行过程中,局部变量通常存储在栈上,动态分配的内存通常在堆上。当程序执行完毕,操作系统会回收程序所占用的内存资源,这些内存中的数据通常会被清除。也有一些特殊情况,比如某些操作系统可能会采用一些技术来缓存部分内存内容,或者某些恶意软件可能会尝试在程序结束后从内存中窃取信息,但从正常的程序设计和运行角度来说,程序结束后,内存中的绝大部分信息会被认为是丢失的。 ↩︎

  2. 在 C++ 11 标准中,引入了 long long 这种新的整数数据类型。虽然一些编译器可能已经支持 long long 作为扩展类型,但它并不是 C++ 标准所规定的类型。C++11 标准将其纳入,使其成为合法的、被标准认可的类型。 ↩︎

  3. 例如将“人”作为“一个类”,他所可能包含的“成员属性”包括:姓名、身高、年龄等,用法(person.name()、person.height()、person.age())。 ↩︎