3.1 简单变量 在第2章中,我们已经接触了变量的声明和使用。本章将深入探讨C++的基本数据类型,首先从用于存储数字和字符的简单变量开始。简单变量是C++中存储数据的基本单元。
3.1.1 变量名 变量名是赋予内存位置的标识符,用于访问存储在该位置的数据。在C++中,选择有意义的变量名是良好编程实践的一部分。
命名规则:
字符集: 变量名只能包含字母(大小写)、数字和下划线 _
。
首字符: 名称的第一个字符不能是数字。
区分大小写: C++是大小写敏感的,myVariable
和 myvariable
是两个不同的变量名。
不能是关键字: 不能使用C++关键字(如 int
, double
, return
, if
, class
等)作为变量名。
长度限制: C++对名称的长度没有硬性规定,但长名称可能会在某些旧编译器或链接器上遇到问题。通常,有意义且不过于冗长的名称是最好的。
下划线的使用:
以两个下划线 __
开头或以下划线和大写字母 _A
到 _Z
开头的名称被保留给编译器及其使用的资源使用。
以下划线 _
开头的名称被保留用作全局标识符。
虽然在某些情况下可以使用以下划线开头的名称(例如在函数内部),但最好避免这种用法,以防与系统使用的名称冲突。
命名约定 (非强制,但推荐):
有意义: 变量名应反映其存储的数据或用途(例如 numberOfStudents
, userName
, totalScore
)。
驼峰命名法 (Camel Case): 从第二个单词开始,每个单词的首字母大写(例如 myVariableName
, studentAge
)。这是C++中常见的风格。
下划线分隔 (Snake Case): 使用下划线分隔单词(例如 my_variable_name
, student_age
)。这也是一种常见的风格。
选择一种风格并保持一致。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> int main () { int age; double accountBalance; char first_initial; long long populationOfEarth; int _internal_counter; int value2; int count = 10 ; int Count = 20 ; std ::cout << "count: " << count << std ::endl ; std ::cout << "Count: " << Count << std ::endl ; return 0 ; }
3.1.2 整型 整型 (Integer) 是没有小数部分的数字。C++提供了多种整型类型来存储整数,它们的主要区别在于占用的内存空间大小以及能够表示的数值范围。
计算机内存由称为位 (bit) 的单元组成。8个位组成一个**字节 (byte)**。每个位可以表示两种状态(通常是0或1)。字节是内存中最小的可寻址单元,意味着每个字节都有一个唯一的地址。
不同的整型类型使用不同数量的字节来存储值。使用的字节数越多,可以表示的整数范围就越大。
基本整型类型:
short
int
long
long long
(C++11 新增)
我们将在下一节详细讨论这些类型。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> int main () { short smallNumber; int standardInteger; long largeInteger; long long veryLargeInteger; smallNumber = 10 ; standardInteger = 10000 ; largeInteger = 1000000 ; veryLargeInteger = 10000000000L L; std ::cout << "short: " << smallNumber << std ::endl ; std ::cout << "int: " << standardInteger << std ::endl ; std ::cout << "long: " << largeInteger << std ::endl ; std ::cout << "long long: " << veryLargeInteger << std ::endl ; return 0 ; }
3.1.3 整型short、int、long和long long C++标准规定了各种整型类型的最小尺寸(占用的内存位数),但具体尺寸可能因编译器和操作系统而异。
标准规定的最小尺寸:
short
: 至少16位。
int
: 至少和 short
一样大,通常是系统处理效率最高的整数长度(例如,在32位系统上通常是32位,64位系统上可能是32位或64位)。
long
: 至少32位,且至少和 int
一样大。
long long
: 至少64位,且至少和 long
一样大。
如何查看具体尺寸:
可以使用 sizeof
运算符来查看特定类型在你的系统上占用的字节数。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> #include <climits> // 包含整型限制信息 (如 INT_MAX) int main () { std ::cout << "Size of short: " << sizeof (short ) << " bytes" << std ::endl ; std ::cout << "Size of int: " << sizeof (int ) << " bytes" << std ::endl ; std ::cout << "Size of long: " << sizeof (long ) << " bytes" << std ::endl ; std ::cout << "Size of long long: " << sizeof (long long ) << " bytes" << std ::endl ; std ::cout << std ::endl ; std ::cout << "Maximum value for int: " << INT_MAX << std ::endl ; short s_value = 32767 ; int i_value = 2000000000 ; long l_value = 1000000000L ; long long ll_value = 50000000000L L; std ::cout << "short value: " << s_value << std ::endl ; std ::cout << "int value: " << i_value << std ::endl ; std ::cout << "long value: " << l_value << std ::endl ; std ::cout << "long long value: " << ll_value << std ::endl ; short max_short = SHRT_MAX; std ::cout << "Max short: " << max_short << std ::endl ; max_short = max_short + 1 ; std ::cout << "Max short + 1: " << max_short << std ::endl ; return 0 ; }
注意: 当一个整数值超出了其类型所能表示的最大范围时,会发生溢出 (Overflow)**。对于有符号整数溢出,C++标准规定其行为是 未定义的 (Undefined Behavior)**,这意味着任何事情都可能发生(程序崩溃、得到奇怪的结果等)。对于无符号整数溢出,行为是定义好的(通常是回绕,即从0重新开始)。
3.1.4 无符号类型 对于每种整型(short
, int
, long
, long long
),都存在一个对应的无符号 (unsigned) 版本。无符号类型只能存储非负整数(0和正数)。
通过在类型名前加上 unsigned
关键字来声明无符号类型。
特点:
范围: 在相同的字节数下,无符号类型可以表示的最大值大约是有符号类型的两倍,因为它们不需要用一位来表示正负号。范围从 0 开始。
用途: 当你知道一个变量永远不会是负数时(例如,计数器、数组索引、人口数量等),使用无符号类型可以增大其可表示的正数范围。
回绕 (Wrap Around): 当无符号整数的值超出其最大范围时,它会从0重新开始(模运算)。例如,如果一个 unsigned short
的最大值是 65535,那么 65535 + 1
会变成 0
。同样,0 - 1
会变成 65535
。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include <climits> // 包含 UINT_MAX 等 int main () { unsigned short us_value; unsigned int ui_value; unsigned long ul_value; unsigned long long ull_value; std ::cout << "Size of unsigned int: " << sizeof (unsigned int ) << " bytes" << std ::endl ; std ::cout << "Maximum value for unsigned int: " << UINT_MAX << std ::endl ; std ::cout << std ::endl ; us_value = 65535 ; ui_value = 4000000000U ; ul_value = 8000000000U L; ull_value = 18000000000000000000U LL; std ::cout << "unsigned short: " << us_value << std ::endl ; std ::cout << "unsigned int: " << ui_value << std ::endl ; std ::cout << "unsigned long: " << ul_value << std ::endl ; std ::cout << "unsigned long long: " << ull_value << std ::endl ; std ::cout << std ::endl ; unsigned short test_wrap = USHRT_MAX; std ::cout << "Max unsigned short: " << test_wrap << std ::endl ; test_wrap = test_wrap + 1 ; std ::cout << "Max unsigned short + 1: " << test_wrap << std ::endl ; test_wrap = 0 ; std ::cout << "Unsigned short = 0" << std ::endl ; test_wrap = test_wrap - 1 ; std ::cout << "Unsigned short - 1: " << test_wrap << std ::endl ; return 0 ; }
注意: 混合使用有符号和无符号整数进行运算时要特别小心,因为有符号数可能会被隐式转换为无符号数,导致意外的结果,尤其是在比较运算中。
3.1.5 选择整型类型 在选择使用哪种整型类型时,应考虑以下因素:
数值范围: 确保所选类型能够容纳你程序中需要存储的最大(和最小,如果是有符号)整数值。如果数值可能很大,优先考虑 long
或 long long
。如果数值永远非负,可以考虑 unsigned
版本以获得更大的正数范围。
内存消耗: 如果内存非常宝贵(例如在嵌入式系统或处理大量数据时),并且确定数值范围较小,可以使用 short
来节省内存。
性能: int
通常被认为是计算机处理效率最高的整数类型。除非有明确的范围或内存需求,否则 int
是一个不错的默认选择。
可移植性: int
的大小可能因系统而异。如果需要确保在不同系统上具有固定的大小,可以使用 C++11 引入的固定宽度整数类型(在 <cstdint>
头文件中定义),例如 int16_t
, uint32_t
, int64_t
等。
代码清晰度: 选择最能自然表达意图的类型。如果变量代表一个永远不会为负的计数,unsigned int
可能比 int
更能表达这个意图。
一般建议:
如果不需要存储负数,且需要更大的正数范围,或者变量逻辑上就是无符号的(如计数),使用 unsigned
。
如果数值范围不大,且没有特殊内存或性能要求,int
是最常用的选择。
如果 int
可能不够大,使用 long long
。long
在某些系统上可能和 int
大小相同,而 long long
保证至少64位。
只有在内存非常受限且确定数值很小时才使用 short
。
为了明确性和跨平台兼容性,可以考虑使用 <cstdint>
中的固定宽度类型。
用法与示例 (选择):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <cstdint> // 包含固定宽度整数类型 int main () { unsigned int studentAge = 20 ; for (int i = 0 ; i < 10 ; ++i) { } unsigned long long fileSize = 5000000000U LL; int32_t preciseCounter = -123456789 ; std ::cout << "Age: " << studentAge << std ::endl ; std ::cout << "File Size: " << fileSize << " bytes" << std ::endl ; std ::cout << "Precise Counter: " << preciseCounter << std ::endl ; return 0 ; }
3.1.6 整型字面值 整型字面值 (Integer Literal) 是直接写在代码中的整数常量,例如 10
, 0
, -5
, 42
。C++允许以不同的进制(基数)书写整型字面值:
十进制 (Decimal): 最常见的形式,以非零数字开头(除非是数字0本身)。例如:10
, 255
, 0
, 12345
。
八进制 (Octal): 以 0
开头。只包含数字 0-7。例如:012
(等于十进制的 10),077
(等于十进制的 63)。现代C++中应谨慎使用,容易与十进制混淆。
十六进制 (Hexadecimal): 以 0x
或 0X
开头。包含数字 0-9 和字母 a-f (或 A-F),大小写不敏感。例如:0xA
(等于十进制的 10),0xFF
(等于十进制的 255),0x1a2b
。常用于表示内存地址或位模式。
二进制 (Binary) (C++14): 以 0b
或 0B
开头。只包含数字 0 和 1。例如:0b1010
(等于十进制的 10),0b11111111
(等于十进制的 255)。
后缀 (Suffixes):
可以通过在字面值后面添加后缀来显式指定其类型:
u
或 U
: 表示 unsigned
类型 (unsigned int
, unsigned long
, unsigned long long
)。
l
或 L
: 表示 long
或 unsigned long
类型。
ll
或 LL
: 表示 long long
或 unsigned long long
类型 (C++11)。
后缀可以组合使用,例如 ul
, UL
, ull
, ULL
, lu
, llu
等(顺序和大小写不重要)。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> int main () { int decimal_val = 100 ; int octal_val = 0144 ; int hex_val = 0x64 ; std ::cout << "Decimal: " << decimal_val << std ::endl ; std ::cout << "Octal (0144): " << octal_val << std ::endl ; std ::cout << "Hexadecimal (0x64): " << hex_val << std ::endl ; unsigned int u_val = 100U ; long l_val = 200000L ; unsigned long ul_val = 300000U L; long long ll_val = 4000000000L L; unsigned long long ull_val = 5000000000U LL; std ::cout << "Unsigned int: " << u_val << std ::endl ; std ::cout << "Long: " << l_val << std ::endl ; std ::cout << "Unsigned long: " << ul_val << std ::endl ; std ::cout << "Long long: " << ll_val << std ::endl ; std ::cout << "Unsigned long long: " << ull_val << std ::endl ; return 0 ; }
3.1.7 C++如何确定常量的类型 当你写下一个整型字面值(常量)而没有指定后缀时,C++编译器会根据其值来推断其类型。
规则:
十进制常量: 编译器会选择能容纳该值的最小的有符号类型,依次尝试:int
, long
, long long
(C++11)。例如:
100
会被认为是 int
(如果 int
能容纳)。
3000000000
会被认为是 long
(如果 int
不能容纳但 long
可以),或者 long long
(如果 int
和 long
都不能容纳但 long long
可以)。
八进制或十六进制常量: 编译器会选择能容纳该值的最小类型,依次尝试:int
, unsigned int
, long
, unsigned long
, long long
(C++11), unsigned long long
(C++11)。注意这里包含了 unsigned
类型。
为什么要知道这个?
这在涉及类型转换和函数重载时可能很重要。如果你传递一个常量给函数,它的默认类型可能会影响哪个重载版本被调用。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> #include <typeinfo> // 用于 typeid (仅作演示) void process (int n) { std ::cout << "Processing int: " << n << std ::endl ; } void process (unsigned int n) { std ::cout << "Processing unsigned int: " << n << std ::endl ; } void process (long n) { std ::cout << "Processing long: " << n << std ::endl ; } void process (unsigned long n) { std ::cout << "Processing unsigned long: " << n << std ::endl ; } void process (long long n) { std ::cout << "Processing long long: " << n << std ::endl ; } void process (unsigned long long n) { std ::cout << "Processing unsigned long long: " << n << std ::endl ; } int main () { std ::cout << "Type of 100: " ; process(100 ); std ::cout << "Type of 3000000000: " ; process(3000000000 ); std ::cout << "Type of 0xFFFFFFFF: " ; process(0xFFFFFFFF ); std ::cout << "Type of 150L: " ; process(150L ); std ::cout << "Type of 200U: " ; process(200U ); std ::cout << "Type of 5000000000LL: " ; process(5000000000L L); return 0 ; }
关键点: 如果不确定常量会被推断成什么类型,或者想要确保它是特定类型,最好使用后缀。
3.1.8 char类型:字符和小整数 char
类型是另一种整型类型,它被设计用来存储**字符 (character)**,例如字母、数字、标点符号等。
特点:
大小: char
通常占用 1 个字节(8位)的内存。这是C++标准保证的 (sizeof(char)
总是 1)。
字符表示: 计算机内部使用数值编码(如 ASCII 或 Unicode 的子集)来表示字符。char
变量存储的是这些字符对应的整数编码。
字符字面值: 使用单引号 ' '
括起来表示单个字符字面值,例如 'A'
, 'a'
, '5'
, '?'
, '\n'
(换行符)。
整数类型: char
本质上仍然是一个整数类型。它可以参与算术运算。
有符号 vs 无符号: char
类型具体是有符号 (signed char
) 还是无符号 (unsigned char
) 取决于编译器实现。如果你需要明确,可以直接使用 signed char
或 unsigned char
。
signed char
: 范围通常是 -128 到 127。
unsigned char
: 范围通常是 0 到 255。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> int main () { char grade = 'A' ; char initial = 'J' ; char symbol = '$' ; char newline = '\n' ; std ::cout << "Your grade is: " << grade << std ::endl ; std ::cout << "Initial: " << initial << std ::endl ; std ::cout << "Symbol: " << symbol << std ::endl ; std ::cout << "Printing a newline:" << newline; std ::cout << "After newline." << std ::endl ; std ::cout << "The integer code for 'A' is: " << int (grade) << std ::endl ; std ::cout << "The integer code for '$' is: " << static_cast <int >(symbol) << std ::endl ; char small_num = 65 ; std ::cout << "Character with code 65: " << small_num << std ::endl ; char next_char = grade + 1 ; std ::cout << "Character after 'A': " << next_char << std ::endl ; char response; std ::cout << "Enter a character: " ; std ::cin >> response; std ::cout << "You entered: " << response << std ::endl ; std ::cout << "Its code is: " << int (response) << std ::endl ; signed char sc = -5 ; unsigned char uc = 250 ; std ::cout << "Signed char: " << int (sc) << std ::endl ; std ::cout << "Unsigned char: " << int (uc) << std ::endl ; return 0 ; }
转义序列 (Escape Sequence): 以反斜杠 \
开头的特殊字符序列,用于表示无法直接输入的字符或具有特殊含义的字符。常用转义序列包括:
\n
: 换行符
\t
: 水平制表符 (Tab)
\r
: 回车符
\\
: 反斜杠本身
\'
: 单引号
\"
: 双引号
\?
: 问号
\0
: 空字符 (Null character)
\xhh
: 用两位十六进制数 hh 表示字符
\ooo
: 用最多三位八进制数 ooo 表示字符
3.1.9 bool类型 bool
类型是C++中的布尔 (Boolean) 类型,用于表示逻辑值:真 (true) 或 **假 (false)**。
特点:
取值: bool
变量只能存储两个值:true
和 false
。这两个是C++关键字。
整数转换:
在需要整数的地方,true
会被转换为 1
,false
会被转换为 0
。
在需要布尔值的地方,任何非零整数值会被转换为 true
,零值会被转换为 false
。指针类型也可以转换为 bool
(空指针为 false
,非空指针为 true
)。
大小: bool
类型的大小没有严格规定,但通常是 1 个字节,即使它只需要 1 位来存储信息。
用途: 主要用于存储条件判断(如 if
语句、循环条件)的结果。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> int main () { bool isReady = true ; bool hasError = false ; bool isEmpty; isEmpty = false ; std ::cout << "isReady (default output): " << isReady << std ::endl ; std ::cout << "hasError (default output): " << hasError << std ::endl ; std ::cout << "isEmpty (default output): " << isEmpty << std ::endl ; std ::cout << std ::boolalpha; std ::cout << "isReady (boolalpha): " << isReady << std ::endl ; std ::cout << "hasError (boolalpha): " << hasError << std ::endl ; std ::cout << "isEmpty (boolalpha): " << isEmpty << std ::endl ; std ::cout << std ::noboolalpha; int ready_int = isReady; int error_int = hasError; std ::cout << "isReady as int: " << ready_int << std ::endl ; std ::cout << "hasError as int: " << error_int << std ::endl ; bool from_int_non_zero = 100 ; bool from_int_zero = 0 ; std ::cout << std ::boolalpha; std ::cout << "Bool from 100: " << from_int_non_zero << std ::endl ; std ::cout << "Bool from 0: " << from_int_zero << std ::endl ; if (isReady) { std ::cout << "System is ready." << std ::endl ; } else { std ::cout << "System is not ready." << std ::endl ; } if (!hasError) { std ::cout << "No errors detected." << std ::endl ; } else { std ::cout << "An error occurred." << std ::endl ; } return 0 ; }
3.2 const限定符 现在我们来探讨一种在C++中创建常量 (Constant) 的更可靠的方法:使用 const
限定符。常量是指在程序运行期间其值不能被修改的量。
const
是一个**类型限定符 (Type Qualifier)**,它用于修改变量的声明,使其成为只读。
作用:
创建符号常量: const
允许你为常量值(如圆周率、最大尝试次数等)赋予一个有意义的名称,提高代码的可读性和可维护性。
防止意外修改: 一旦变量被声明为 const
并初始化后,编译器会阻止任何试图修改该变量值的代码。这有助于防止因意外赋值而导致的错误。
类型安全: 与使用 #define
创建宏常量相比,const
常量具有明确的数据类型,编译器可以对其进行类型检查,更加安全。
作用域: const
常量遵循标准的作用域规则(例如,在函数内部定义的 const
常量只在该函数内有效),而 #define
宏是全局替换。
声明和初始化:
声明 const
变量的语法是在类型名前或类型名后加上 const
关键字。关键在于,const
变量必须在声明时进行初始化 ,因为之后就不能再给它赋值了。
1 2 3 const type variableName = value; type const variableName = value;
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> int main () { const int MONTHS_IN_YEAR = 12 ; const double PI = 3.14159 ; const char * GREETING = "Hello, world!" ; std ::cout << "Months in a year: " << MONTHS_IN_YEAR << std ::endl ; double radius = 5.0 ; double circumference = 2 * PI * radius; std ::cout << "Circumference: " << circumference << std ::endl ; if (true ) { const int MAX_TRIES = 3 ; std ::cout << "Max tries inside block: " << MAX_TRIES << std ::endl ; } return 0 ; }
const
与 #define
的比较:
在 C 语言中,通常使用 #define
预处理器指令来创建符号常量:
1 #define MONTHS_IN_YEAR 12
虽然 #define
也能达到类似目的,但 const
在 C++ 中通常是更好的选择:
类型安全: const
常量有明确的数据类型,编译器会进行类型检查。#define
只是简单的文本替换,没有类型信息,可能导致意外错误。
作用域: const
常量遵循 C++ 的作用域规则,可以创建局部常量。#define
宏通常是全局的(从定义点到文件尾),容易造成命名冲突。
调试: const
常量在调试器中通常可见,有符号名。#define
宏在编译前就被替换掉了,调试时可能只能看到替换后的字面值。
类作用域: const
可以用于定义类作用域内的常量(类成员),而 #define
不能直接做到这一点。
一般建议: 在 C++ 中,优先使用 const
来定义符号常量,而不是 #define
。
3.3 浮点数 浮点数是C++中用于表示带小数部分的数字(实数)的类型。它们可以表示非常大或非常小的数值,以及整数无法表示的小数。
3.3.1 书写浮点数 C++允许使用两种方式来书写浮点字面值(常量):
标准小数点表示法 (Standard Decimal Point Notation):
包含一个小数点。
例如:12.34
, 0.0
, 99.
, .5
(等同于 0.5), -1.67
。
E表示法 (E Notation) 或科学记数法 (Scientific Notation):
用于表示非常大或非常小的数。
格式为:mantissaEexponent
或 mantissaeexponent
。
mantissa
(尾数) 是一个数字(可以带小数点)。
E
或 e
表示 “乘以10的…次幂”。
exponent
(指数) 是一个整数(可以为负)。
例如:
3.45E6
表示 3.45 * 106 (即 3,450,000)。
2.5e-4
表示 2.5 * 10-4 (即 0.00025)。
9E12
表示 9 * 1012 。
-1.23e+3
表示 -1.23 * 103 (即 -1230)。
注意:
E表示法中,E
或 e
前后不能有空格。
指数必须是整数。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> int main () { double price = 99.99 ; double temperature = -15.5 ; double small_fraction = .25 ; double earth_mass = 5.972E24 ; double electron_charge = -1.602e-19 ; double large_number = 1.0e9 ; std ::cout << "Price: " << price << std ::endl ; std ::cout << "Temperature: " << temperature << std ::endl ; std ::cout << "Small fraction: " << small_fraction << std ::endl ; std ::cout << "Earth mass: " << earth_mass << " kg" << std ::endl ; std ::cout << "Electron charge: " << electron_charge << " C" << std ::endl ; std ::cout << "Large number: " << large_number << std ::endl ; std ::cout .setf(std ::ios_base::fixed, std ::ios_base::floatfield); std ::cout << "Large number (fixed): " << large_number << std ::endl ; return 0 ; }
3.3.2 浮点类型 C++提供了三种浮点类型,它们在精度(有效位数)和存储范围上有所不同:
float
: 单精度浮点数。通常占用 4 个字节 (32位)。精度较低,范围较小。
double
: 双精度浮点数。通常占用 8 个字节 (64位)。精度和范围比 float
大很多。这是C++中最常用的浮点类型,浮点常量(如 3.14
)默认就是 double
类型。
long double
: 扩展精度浮点数。通常占用 8、10、12 或 16 个字节,具体取决于编译器和系统。提供最高的精度和最大的范围。
精度和范围:
精度 (Precision): 指的是可以表示的有效数字的位数。
float
通常保证至少 6 位有效数字。
double
通常保证至少 15 位有效数字。
long double
通常提供比 double
更高的精度。
范围 (Range): 指的是可以表示的最小和最大数值。double
的范围远大于 float
,long double
的范围通常更大。
可以使用 <cfloat>
(或 C 语言的 <float.h>
) 头文件中的常量来查看具体系统的精度和范围限制(例如 FLT_DIG
, DBL_DIG
, LDBL_DIG
表示有效位数)。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <cfloat> // 包含浮点数限制信息 int main () { std ::cout << "Size of float: " << sizeof (float ) << " bytes" << std ::endl ; std ::cout << "Size of double: " << sizeof (double ) << " bytes" << std ::endl ; std ::cout << "Size of long double: " << sizeof (long double ) << " bytes" << std ::endl ; std ::cout << std ::endl ; std ::cout << "Digits of precision for float: " << FLT_DIG << std ::endl ; std ::cout << "Digits of precision for double: " << DBL_DIG << std ::endl ; std ::cout << "Digits of precision for long double: " << LDBL_DIG << std ::endl ; std ::cout << std ::endl ; float f_pi = 3.14159265f ; double d_pi = 3.141592653589793 ; long double ld_pi = 3.14159265358979323846L ; std ::cout .precision(20 ); std ::cout << "float pi: " << f_pi << std ::endl ; std ::cout << "double pi: " << d_pi << std ::endl ; std ::cout << "long double pi: " << ld_pi << std ::endl ; return 0 ; }
选择建议:
除非有特别的内存或性能考虑,并且确定 float
的精度足够,否则**优先使用 double
**。它是C++浮点计算的常用类型。
当需要极高的精度或非常大的数值范围时,使用 long double
。
3.3.3 浮点常量 浮点常量(字面值)就是直接写在代码中的浮点数值,例如 3.14
, 1.0
, -2.5e8
。
默认类型: 默认情况下,C++将不带后缀的浮点常量视为 double
类型。
后缀 (Suffixes): 可以通过添加后缀来显式指定浮点常量的类型:
f
或 F
: 表示 float
类型。
l
或 L
: 表示 long double
类型。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> #include <typeinfo> // 用于 typeid (仅作演示) int main () { std ::cout << "Type of 3.14: " << typeid (3.14 ).name() << std ::endl ; std ::cout << "Type of 1.0e-5: " << typeid (1.0e-5 ).name() << std ::endl ; std ::cout << "Type of 3.14f: " << typeid (3.14f ).name() << std ::endl ; std ::cout << "Type of 3.14F: " << typeid (3.14F ).name() << std ::endl ; std ::cout << "Type of 3.14l: " << typeid (3.14l ).name() << std ::endl ; std ::cout << "Type of 3.14L: " << typeid (3.14L ).name() << std ::endl ; float price = 99.99f ; double weight = 75.5 ; long double distance = 1.23e10 L; std ::cout << "Price: " << price << std ::endl ; std ::cout << "Weight: " << weight << std ::endl ; std ::cout << "Distance: " << distance << std ::endl ; return 0 ; }
(注意: typeid().name()
的输出可能因编译器而异,但可以大致看出类型)
在将常量赋给 float
变量时,使用 f
或 F
后缀是一个好习惯,可以避免编译器产生关于从 double
转换到 float
可能丢失精度的警告。
3.3.4 浮点数的优缺点 浮点数在表示实数方面非常有用,但也存在一些固有的限制和需要注意的地方。
优点:
表示范围广: 可以表示比整型大得多或小得多的数值。
表示小数: 可以表示整数无法表示的小数部分。
标准化: 大多数现代计算机都遵循 IEEE 754 标准来表示和处理浮点数,这提高了可移植性。
缺点:
精度限制: 浮点数只能近似 地表示大多数实数。由于内部使用二进制表示,某些在十进制下看起来很精确的小数(如 0.1)在二进制浮点表示中可能是无限循环小数,只能存储一个近似值。这会导致微小的**舍入误差 (Rounding Error)**。
比较困难: 由于精度限制,直接使用 ==
来比较两个浮点数是否相等通常是不可靠的。微小的舍入误差可能导致逻辑上应该相等的两个数在内部表示上略有不同。比较浮点数时,通常应该检查它们的差值是否在一个很小的容差 (Tolerance) 范围内。
运算速度: 浮点运算通常比整型运算慢(尽管现代处理器有专门的浮点单元来优化)。
特殊值: IEEE 754 标准定义了一些特殊值,如 NaN
(Not a Number,例如 0.0/0.0 的结果) 和无穷大 (Infinity
,例如 1.0/0.0 的结果),需要在使用时注意处理。
用法与示例 (精度问题和比较):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <cmath> // 为了 fabs (计算绝对值) #include <iomanip> // 为了 setprecision int main () { double a = 0.1 ; double b = 0.2 ; double sum = a + b; std ::cout << std ::setprecision(20 ); std ::cout << "a = " << a << std ::endl ; std ::cout << "b = " << b << std ::endl ; std ::cout << "a + b = " << sum << std ::endl ; if (sum == 0.3 ) { std ::cout << "Comparison (==): sum is equal to 0.3" << std ::endl ; } else { std ::cout << "Comparison (==): sum is NOT equal to 0.3" << std ::endl ; } const double TOLERANCE = 1e-9 ; if (std ::fabs (sum - 0.3 ) < TOLERANCE) { std ::cout << "Comparison (tolerance): sum is close enough to 0.3" << std ::endl ; } else { std ::cout << "Comparison (tolerance): sum is NOT close enough to 0.3" << std ::endl ; } float x = 1.0f / 3.0f ; float y = 3.0f * x; std ::cout << "y = 3.0f * (1.0f / 3.0f) = " << y << std ::endl ; return 0 ; }
总结: 在使用浮点数时,要意识到它们是近似值,并避免直接进行相等性比较。在需要精确计算(如金融计算)的场合,可能需要使用专门的库或定点数表示法。对于大多数科学和工程计算,double
提供了足够的精度和范围。
3.4 C++算术运算符 C++提供了丰富的运算符来执行算术计算。本节将介绍基本的算术运算符、它们的优先级和结合性、整数除法和求模运算、类型转换以及 C++11 引入的 auto
类型推断。
基本算术运算符:
+
: 加法 (Addition)
-
: 减法 (Subtraction)
*
: 乘法 (Multiplication)
/
: 除法 (Division)
%
: 求模 (Modulo) 或求余 (Remainder)
这些运算符可以用于 C++ 的所有数值类型(整型和浮点型),但求模运算符 %
通常只用于整数类型。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> int main () { double price = 15.50 ; double tax_rate = 0.08 ; int quantity = 3 ; int total_items = 17 ; int items_per_box = 5 ; double total_price = price * quantity + 5.0 ; std ::cout << "Total price (with fee): " << total_price << std ::endl ; double price_before_tax = price / (1 + tax_rate); std ::cout << "Price before tax: " << price_before_tax << std ::endl ; double tax_amount = price_before_tax * tax_rate; std ::cout << "Tax amount: " << tax_amount << std ::endl ; double average_price = total_price / quantity; std ::cout << "Average price per item: " << average_price << std ::endl ; int full_boxes = total_items / items_per_box; std ::cout << "Full boxes: " << full_boxes << std ::endl ; int remaining_items = total_items % items_per_box; std ::cout << "Remaining items: " << remaining_items << std ::endl ; double discount = -10.0 ; std ::cout << "Discount: " << discount << std ::endl ; double positive_value = +price; std ::cout << "Positive value: " << positive_value << std ::endl ; return 0 ; }
3.4.1 运算符优先级和结合性 当一个表达式中包含多个运算符时,优先级 (Precedence) 和 结合性 (Associativity) 决定了运算的执行顺序。
优先级: 哪个运算符先执行。优先级高的运算符先于优先级低的运算符执行。例如,乘法和除法的优先级高于加法和减法。
结合性: 当多个具有相同优先级的运算符连续出现时,它们的执行顺序。
左结合性 (Left-to-Right): 运算从左向右执行。大多数二元算术运算符(+
, -
, *
, /
, %
)都是左结合的。例如 a - b + c
等价于 (a - b) + c
。
右结合性 (Right-to-Left): 运算从右向左执行。赋值运算符 =
和一元运算符(如取负 -
)是右结合的。例如 a = b = c
等价于 a = (b = c)
。
常见算术运算符优先级 (由高到低):
一元运算符: +
(正号), -
(负号) - (右结合)
乘法、除法、求模: *
, /
, %
- (左结合)
加法、减法: +
, -
- (左结合)
赋值运算符: =
- (右结合)
使用括号: 可以使用圆括号 ()
来覆盖默认的优先级和结合性,强制按特定顺序执行运算。括号内的表达式总是最先计算。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> int main () { int a = 5 , b = 8 , c = 3 , d = 2 ; int result1 = a + b * c; std ::cout << "a + b * c = " << result1 << std ::endl ; int result2 = (a + b) * c; std ::cout << "(a + b) * c = " << result2 << std ::endl ; int result3 = b / c * d; std ::cout << "b / c * d = " << result3 << std ::endl ; int x, y, z; x = y = z = 10 ; std ::cout << "x=" << x << ", y=" << y << ", z=" << z << std ::endl ; int val = - - 5 ; std ::cout << "- - 5 = " << val << std ::endl ; return 0 ; }
建议: 当表达式复杂或优先级不明确时,使用括号来明确运算顺序,可以提高代码的可读性并避免错误。
3.4.2 除法分支 C++ 的除法运算符 /
的行为取决于其操作数 (Operand) 的类型:
浮点数除法: 如果操作数中至少有一个是浮点类型 (float
, double
, long double
),则执行浮点数除法,结果也是浮点类型,包含小数部分。
整数除法: 如果两个操作数都是整数类型 (int
, short
, long
, char
, bool
等),则执行整数除法。结果只保留商的整数部分,小数部分被**截断 (truncated)**(直接丢弃,不是四舍五入)。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> int main () { double result_f1 = 9.0 / 5.0 ; double result_f2 = 9.0 / 5 ; double result_f3 = 9 / 5.0 ; float result_f4 = 9.0f / 5.0f ; std ::cout << "9.0 / 5.0 = " << result_f1 << std ::endl ; std ::cout << "9.0 / 5 = " << result_f2 << std ::endl ; std ::cout << "9 / 5.0 = " << result_f3 << std ::endl ; std ::cout << "9.0f / 5.0f = " << result_f4 << std ::endl ; int result_i1 = 9 / 5 ; int result_i2 = 10 / 3 ; int result_i3 = 13 / 4 ; int result_i4 = -10 / 3 ; std ::cout << "9 / 5 = " << result_i1 << std ::endl ; std ::cout << "10 / 3 = " << result_i2 << std ::endl ; std ::cout << "13 / 4 = " << result_i3 << std ::endl ; std ::cout << "-10 / 3 = " << result_i4 << std ::endl ; double result_mixed = double (9 ) / 5 ; std ::cout << "double(9) / 5 = " << result_mixed << std ::endl ; return 0 ; }
注意: 进行除法运算时,务必清楚操作数的类型,以确保得到期望的结果(整数截断或浮点小数)。
3.4.3 求模运算符 求模运算符 %
计算整数除法的**余数 (Remainder)**。它要求两个操作数都必须是整数类型(或可以转换为整数的类型,如 char
, bool
)。
运算规则: a % b
的结果是 a
除以 b
后的余数。其符号通常与被除数 a
的符号相同(C++11 标准规定如此)。
数学关系: 对于整数 a
和 b
(其中 b != 0
),以下关系通常成立:(a / b) * b + (a % b) == a
用途:
判断一个数是否能被另一个数整除(如果 a % b == 0
,则 a
能被 b
整除)。
获取一个数的最低位数字(num % 10
)。
将数值限制在一个范围内(例如,生成 0 到 N-1 之间的数:value % N
)。
周期性操作(例如,每隔 M 个元素执行一次操作:if (count % M == 0)
)。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> int main () { int total_seconds = 135 ; int seconds_per_minute = 60 ; int minutes = total_seconds / seconds_per_minute; int remaining_seconds = total_seconds % seconds_per_minute; std ::cout << total_seconds << " seconds is " << minutes << " minutes and " << remaining_seconds << " seconds." << std ::endl ; int number = 21 ; if (number % 2 == 0 ) { std ::cout << number << " is even." << std ::endl ; } else { std ::cout << number << " is odd." << std ::endl ; } int value = 123 ; int last_digit = value % 10 ; std ::cout << "The last digit of " << value << " is " << last_digit << std ::endl ; int result1 = 10 % 3 ; int result2 = -10 % 3 ; int result3 = 10 % -3 ; int result4 = -10 % -3 ; std ::cout << "10 % 3 = " << result1 << std ::endl ; std ::cout << "-10 % 3 = " << result2 << std ::endl ; std ::cout << "10 % -3 = " << result3 << std ::endl ; std ::cout << "-10 % -3 = " << result4 << std ::endl ; return 0 ; }
3.4.4 类型转换 C++允许在不同数据类型之间进行转换,这称为类型转换 (Type Casting)**。转换可以 隐式 (Implicitly)** 发生(由编译器自动完成),也可以显式 (Explicitly) 进行(由程序员通过代码指定)。
隐式类型转换 (Automatic Conversion):
在以下情况下,编译器会自动执行类型转换:
混合类型表达式: 当一个表达式中包含不同数值类型的操作数时,较小或较低优先级的类型通常会被提升 (Promoted) 为较大或较高优先级的类型,然后进行运算。
整型提升 (Integral Promotion): 比 int
小的整型(bool
, char
, signed char
, unsigned char
, short
, unsigned short
)在表达式中通常会被提升为 int
(如果 int
能容纳其所有值) 或 unsigned int
。
算术转换 (Usual Arithmetic Conversions): 在涉及不同算术类型(整型和浮点型)的运算中,遵循一套规则将操作数转换为共同的类型(通常是两者中“更宽”或精度更高的类型)。例如,int
和 double
运算时,int
会被转换为 double
。float
和 double
运算时,float
会被转换为 double
。
赋值: 将一个类型的值赋给另一种类型的变量时,右侧的值会被转换为左侧变量的类型。这可能导致精度损失(如 double
转 int
)或范围问题(如 long
转 short
)。
函数参数传递: 将参数传递给函数时,如果实参类型与形参类型不匹配,会尝试进行转换。
函数返回值: 从函数返回一个值时,如果返回值类型与函数声明的返回类型不匹配,会尝试进行转换。
显式类型转换 (Explicit Casting):
当需要强制进行类型转换,或者为了使代码意图更清晰时,可以使用显式类型转换。C++提供了多种转换方式:
C 风格强制类型转换 (C-Style Cast):
1 2 (typeName) expression typeName (expression)
这种方式简单直接,但在某些情况下不够安全,因为它可能执行多种不同类型的转换(如 static_cast
, const_cast
, reinterpret_cast
的组合)。
C++ 类型转换运算符 (C++ Cast Operators): (更推荐,更安全,意图更明确)
static_cast<typeName>(expression)
: 用于比较“自然”和安全的转换,如数值类型之间的转换(整数与浮点数互转、整数与整数互转)、指针类型之间的相关转换(如 void*
与其他类型指针互转、基类指针与派生类指针互转,但需要谨慎)。这是最常用的 C++ 转换符。
dynamic_cast<typeName>(expression)
: 主要用于处理类继承层次结构中的指针或引用转换(向下转型),并在运行时进行类型检查。如果转换无效,对于指针会返回 nullptr
,对于引用会抛出 std::bad_cast
异常。需要基类是多态的(至少有一个虚函数)。
const_cast<typeName>(expression)
: 用于添加或移除表达式的 const
或 volatile
限定符。通常用于去除 const
,但修改原本是 const
的对象是未定义行为。主要用于处理常量性不匹配的旧 API。
reinterpret_cast<typeName>(expression)
: 用于低级别的、通常与实现相关的、不安全的转换。例如,在整数和指针之间进行转换,或者在不相关的指针类型之间进行转换。应极力避免使用,除非确实理解其底层含义和风险。
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> int main () { int i_val = 10 ; double d_val = 3.14 ; short s_val = 5 ; double result1 = i_val + d_val; std ::cout << "int + double = " << result1 << std ::endl ; char c_val = 'A' ; int result2 = c_val + s_val; std ::cout << "char + short = " << result2 << std ::endl ; int i_from_d = d_val; std ::cout << "int from double = " << i_from_d << std ::endl ; short s_from_i = 100000 ; std ::cout << "short from large int = " << s_from_i << std ::endl ; int total = 19 ; int count = 5 ; double average1 = (double )total / count; double average2 = double (total) / count; std ::cout << "(double)total / count = " << average1 << std ::endl ; std ::cout << "double(total) / count = " << average2 << std ::endl ; double average3 = static_cast <double >(total) / count; std ::cout << "static_cast<double>(total) / count = " << average3 << std ::endl ; int char_code = static_cast <int >('B' ); std ::cout << "Code for 'B': " << char_code << std ::endl ; long addr = 1000 ; return 0 ; }
建议:
尽量避免不必要的类型转换。
优先使用 C++ 的 static_cast
进行明确且相对安全的数值或相关指针转换。
谨慎使用 C 风格转换,因为它隐藏了转换的类型和风险。
仅在绝对必要且理解后果的情况下使用 const_cast
和 reinterpret_cast
。
注意隐式转换可能导致的精度损失或意外行为,尤其是在混合有符号和无符号整数时。
3.4.5 C++11中的auto声明 C++11 引入了 auto
关键字,它允许编译器根据变量的初始化表达式 (Initializer) 自动推断出变量的类型。这可以简化代码,尤其是在处理复杂类型(如 STL 迭代器或模板类型)时。
工作原理:
当你使用 auto
声明变量时,必须提供一个初始化表达式。编译器会查看这个表达式的类型,并将该类型赋予 auto
声明的变量。
1 auto variableName = initializationExpression;
要点:
必须初始化: 使用 auto
声明的变量必须在声明时初始化。
类型推断: 类型是从初始化表达式推断出来的,而不是变量本身的某种默认类型。
const
和引用: auto
通常不会自动推断出顶层的 const
或引用。如果需要 const
或引用,需要显式添加。
auto x = value;
// x 的类型与 value 相同 (const/引用被剥离)
const auto cx = value;
// cx 是 const 类型
auto& rx = value;
// rx 是引用类型
const auto& crx = value;
// crx 是 const 引用类型
列表初始化: 对于 C++11 中的列表初始化 {}
,auto
的推断规则比较特殊。
auto x = {1, 2, 3};
// C++11/14: x 被推断为 std::initializer_list
auto y = {1};
// C++11/14: y 被推断为 std::initializer_list
auto z{1};
// C++17: z 被推断为 int (注意没有等号)
用法与示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <vector> // 包含 vector #include <typeinfo> // 用于 typeid (仅作演示) int main () { auto i = 10 ; auto d = 3.14 ; auto f = 3.14f ; auto c = 'A' ; auto b = true ; auto ll = 1234567890L L; std ::cout << "Type of i: " << typeid (i).name() << std ::endl ; std ::cout << "Type of d: " << typeid (d).name() << std ::endl ; std ::cout << "Type of f: " << typeid (f).name() << std ::endl ; std ::cout << "Type of ll: " << typeid (ll).name() << std ::endl ; auto sum = i + d; std ::cout << "Type of sum: " << typeid (sum).name() << std ::endl ; std ::cout << "sum = " << sum << std ::endl ; int original = 100 ; auto copy = original; const auto const_copy = original; auto & ref = original; const auto & const_ref = original; copy = 200 ; ref = 300 ; std ::cout << "original: " << original << std ::endl ; std ::vector <std ::string > names = {"Alice" , "Bob" , "Charlie" }; auto it = names.begin(); std ::cout << "First name: " << *it << std ::endl ; std ::cout << "Type of it: " << typeid (it).name() << std ::endl ; auto list1 = {10 , 20 , 30 }; std ::cout << "Type of list1: " << typeid (list1).name() << std ::endl ; return 0 ; }
优点:
减少冗余代码,尤其是在类型名称很长时。
提高代码的可维护性,如果初始化表达式的类型改变,auto
变量的类型会自动更新。
有助于泛型编程。
缺点/注意事项:
过度使用可能降低代码的可读性,因为读者需要查看初始化表达式才能确定类型。
auto
推断出的类型可能不是你期望的(例如,忘记添加 &
得到副本而不是引用)。
对于代理类(Proxy Classes),auto
可能推断出代理类型而不是期望的值类型,需要小心。
建议: 在类型冗长、明显或无关紧要时使用 auto
可以提高效率。在类型对于理解代码逻辑很重要时,显式写出类型可能更好。
3.5 总结 本章深入探讨了C++用于处理数据的基本内置类型。我们首先学习了简单变量的命名规则和约定,强调了名称的可读性和合法性。
接着,我们详细研究了C++的整型家族 ,包括 short
、int
、long
和 C++11 新增的 long long
。我们讨论了它们各自的大小、表示范围以及如何选择合适的类型。我们还介绍了 unsigned
类型,它们用于存储非负整数,并具有更大的正数范围。我们学习了如何书写不同进制(十进制、八进制、十六进制,以及C++14的二进制)的整型字面值,以及如何使用后缀(U
, L
, LL
)来指定常量的具体类型,并了解了编译器在没有后缀时如何推断常量类型。
char
类型被介绍为一种特殊的整型,主要用于存储字符,但也可以作为小整数使用。我们学习了字符字面值(使用单引号)和转义序列。bool
类型也被引入,用于表示逻辑真 (true
) 和假 (false
),以及它与整数(1和0)之间的转换关系。
为了创建不可修改的变量(常量),我们学习了 const
限定符。使用 const
定义的常量必须在声明时初始化,它提供了类型安全和作用域控制,是比 #define
更受推荐的常量定义方式。
然后,我们转向了浮点类型 (float
, double
, long double
),用于表示带小数的数字。我们学习了书写浮点数的两种方式(标准小数点和E表示法),了解了不同浮点类型的精度和范围差异,以及如何使用后缀(f
, L
)指定浮点常量类型(默认为 double
)。我们还讨论了浮点数的优点(范围广、表示小数)和固有的缺点(精度限制、比较困难)。
最后,我们学习了C++的基本算术运算符 (+
, -
, *
, /
, %
)。我们探讨了运算符的优先级和结合性规则,以及如何使用括号来控制运算顺序。特别地,我们区分了整数除法(结果截断)和浮点数除法,并学习了求模运算符 %
的用法(主要用于整数求余)。
类型转换 是本章的另一个重点,包括编译器自动执行的隐式转换(如整型提升和算术转换)和程序员指定的显式转换(C风格转换和更安全的C++转换符 static_cast
、dynamic_cast
、const_cast
、reinterpret_cast
)。我们强调了转换中可能出现的信息丢失问题。
C++11 引入的 auto
关键字也被介绍,它允许编译器根据初始化表达式自动推断变量类型,简化了代码,尤其是在处理复杂类型时。
通过本章的学习,我们掌握了C++的基本数据类型及其用法,为后续更复杂的数据结构和算法打下了坚实的基础。