W2 数据类型、变量与常量、运算符与表达式
基础知识
- 计算机基本组成(运算器、控制器、存储器、输入输出)
- 进制、基数、位权
- 位与字节
- 2、8、16、10 进制之间的相互转换
数据类型
- 常量:宏 vs 常变量(
const
) - 变量:全局变量(堆)vs 局部变量(初始化)
数据类型的分类
- 基本类型
- 整型
- 短整型
short int
- 整型
int
- 长整型
long int
- 长长整型
long long int
- 短整型
- 字符型
char
- 浮点型
- 单精度型
float
- 双精度型
double
- 长双精度型
long double
- 单精度型
- 布尔型
bool
- 整型
- 派生类型
- 指针类型
*
- 枚举类型
enum
- 数组类型
[]
- 结构体类型
struct
- 共用体类型
union
- 类类型
class
- 指针类型
- 空类型
void
long double
和 double
的关系?
long double
是 16 位字长时代遗留下来的。在 32 位和 64 位计算机中,long double
与 double
完全等价,保留 long double
只是为了向后兼容。
数据类型的字节数与范围
有关不同编译器下数据类型字节数可能不同的说明
不同的编译系统中,不同数据类型的所占字节 / 表示范围可能不同。所有 C/C++ 编译器中不同整型数据的相互关系仅遵循一个基本原则:以 int
为基准,short
不大于 int
,long
不小于 int
,long long
不小于 long
。
本课程中如不加以说明,均以 VC++(下表)为准。
类型 | 标识符 | 字节 | 范围 |
---|---|---|---|
整型 | [signed] int | 4 | |
无符号整型 | unsigned [int] | 4 | |
短整型 | short [int] | 2 | |
无符号短整型 | unsigned short [int] | 2 | |
长整型 | long [int] | 4 | |
无符号长整型 | unsigned long [int] | 4 | |
长长整型 | long long [int] | 8 | |
无符号长长整型 | unsigned long long [int] | 8 | |
字符型 | [signed] char | 1 | |
无符号字符型 | unsigned char | 1 | |
单精度型 | float | 4 | |
双精度型 | double | 8 | |
长双精度型 | long double | 8 |
课程使用的 GCC 9.2 也遵循此表。
使用 sizeof(类型)
来查询数据类型所占的字节。
#include <iostream>
using namespace std;
int main() {
cout << sizeof(int) << endl // 4
<< sizeof(unsigned int) << endl // 4
<< sizeof(long) << endl // 4
<< sizeof(unsigned short) << endl // 2
<< sizeof(unsigned long long) << endl // 8
<< sizeof(float) << endl // 4
<< sizeof(long double) << endl; // 16
return 0;
}
- 整型中,对于
个字节的值: - 有符号整型使用补码方式存储
- 正数因符号位留空,最大值为
- 补码可以看作最高位表示
其余照常,因此负数最小为 - 总体范围为
- 正数因符号位留空,最大值为
- 无符号整型全为原码
- 没有符号位,最大值为所有位全
1
,最大值为 - 范围为
- 没有符号位,最大值为所有位全
- 有符号整型使用补码方式存储
- 浮点型中
- 单精度为 1 位符号位、8 位阶码和 23 位尾数
- 双精度为 1 位符号位、11 位阶码和 52 位尾数
与整型有关的更多内容可参考 JavaScript 位运算 - 重温整数 和 对补码的本质理解,与浮点型有关的内容可参考 JavaScript 数值存储与安全 - 存储格式。
可以使用 climits
打印数据类型的上下限:
#include <climits>
#include <iostream>
using namespace std;
int main() {
cout << INT_MIN << endl // -2147483648
<< UINT_MAX << endl // 4294967295
<< LLONG_MAX << endl // 9223372036854775807
<< SHRT_MAX << endl; // 32767
return 0;
}
数值字面量
进制前缀
- 十进制:无前缀,如
123
- 二进制:加前缀
0b
或0B
,如0b1111011
- 八进制:加前缀
0
,如0173
- 十六进制:加前缀
0x
或0X
,如0x7b
cout << 123 << endl // 123
<< 0b1111011 << endl // 123
<< 0173 << endl // 123
<< 0x7b << endl; // 123
WARNING
C++ 中不存在 0o
或 0O
前缀表示八进制的语法!
类型后缀
- 整型
- 不加后缀,默认为
int
型 - 加
l
或L
为long
- 加
u
或U
为unsigned
- 不加后缀,默认为
- 浮点
- 不加后缀,默认为
double
型 - 加后缀
f
或F
为float
型
- 不加后缀,默认为
C++ 提供 typeid
运算符用于查询值的类型,使用方式为 typeid(值).name()
:
// GCC VC++
cout << typeid(123).name() << endl // i int
<< typeid(123l).name() << endl // l long
<< typeid(123lu).name() << endl // m unsigned long
<< typeid(123ul).name() << endl // m unsigned long
<< typeid(123.0).name() << endl // d double
<< typeid(123.0f).name() << endl; // f float
其中 VC++ 与 GCC 的输出不同。
VC++ 与 GCC 类型输出对照表
类型 | VC++ 输出 | GCC 输出 |
---|---|---|
bool | bool | b |
char | char | c |
unsigned char | unsigned char | h |
signed short | short | s |
unsigned short | unsigned short | t |
int | int | i |
unsigned | unsigned int | j |
unsigned long | unsigned long | m |
long long | __int64 | x |
unsigned long long | unsigned __int64 | y |
float | float | f |
double | double | d |
long double | long double | e |
可使用下面代码验证。
#include <iostream>
using namespace std;
int main() {
cout << typeid(bool).name() << endl;
cout << typeid(char).name() << endl;
cout << typeid(unsigned char).name() << endl;
cout << typeid(signed short).name() << endl;
cout << typeid(unsigned short).name() << endl;
cout << typeid(int).name() << endl;
cout << typeid(unsigned).name() << endl;
cout << typeid(unsigned long).name() << endl;
cout << typeid(long long).name() << endl;
cout << typeid(unsigned long long).name() << endl;
cout << typeid(float).name() << endl;
cout << typeid(double).name() << endl;
cout << typeid(long double).name() << endl;
return 0;
}
科学计数法
同其他常见语言,使用 [+-]?(\d+\.?\d*|\.\d+)[Ee][+-]?\d+
科学计数法形式表达数值字面量,即可以使用 e
或 E
表示 “
cout << 1.2e5 << endl; // 120000
字符与字符串字面量
存储与编码
使用单引号 '
包裹字符,使用 "
包裹字符串(字符序列\0
。
C/C++ 对「字符」的定义是单字节的,可以简单理解为 ASCII 字符。对于汉字等多字节字符,不能用单个 char
保存。
char ch = '文';
// [warning] multi-character character constant [-Wmultichar]
// [warning] overflow in conversion from 'int' to 'char'
ASCII 码表
DEC | Char | DEC | Char | DEC | Char | DEC | Char | DEC | Char |
---|---|---|---|---|---|---|---|---|---|
0 | NUL | 26 | SUB | 52 | 4 | 78 | N | 104 | h |
1 | SOH | 27 | ESC | 53 | 5 | 79 | O | 105 | i |
2 | STX | 28 | FS | 54 | 6 | 80 | P | 106 | j |
3 | ETX | 29 | GS | 55 | 7 | 81 | Q | 107 | k |
4 | EOT | 30 | RS | 56 | 8 | 82 | R | 108 | l |
5 | ENQ | 31 | US | 57 | 9 | 83 | S | 109 | m |
6 | ACK | 32 | | 58 | : | 84 | T | 110 | n |
7 | BEL | 33 | ! | 59 | ; | 85 | U | 111 | o |
8 | BS | 34 | " | 60 | < | 86 | V | 112 | p |
9 | HT | 35 | # | 61 | = | 87 | W | 113 | q |
10 | LF | 36 | $ | 62 | > | 88 | X | 114 | r |
11 | VT | 37 | % | 63 | ? | 89 | Y | 115 | s |
12 | FF | 38 | & | 64 | @ | 90 | Z | 116 | t |
13 | CR | 39 | ' | 65 | A | 91 | [ | 117 | u |
14 | SO | 40 | ( | 66 | B | 92 | \ | 118 | v |
15 | SI | 41 | ) | 67 | C | 93 | ] | 119 | w |
16 | DLE | 42 | * | 68 | D | 94 | ^ | 120 | x |
17 | DC1 | 43 | + | 69 | E | 95 | _ | 121 | y |
18 | DC2 | 44 | , | 70 | F | 96 | | | 122 | z |
19 | DC3 | 45 | - | 71 | G | 97 | a | 123 | { |
20 | DC4 | 46 | . | 72 | H | 98 | b | 124 | | |
21 | NAK | 47 | / | 73 | I | 99 | c | 125 | } |
22 | SYN | 48 | 0 | 74 | J | 100 | d | 126 | ~ |
23 | ETB | 49 | 1 | 75 | K | 101 | e | 127 | DEL |
24 | CAN | 50 | 2 | 76 | L | 102 | f | ||
25 | EM | 51 | 3 | 77 | M | 103 | g |
字符转义
C++ 字面量字符转义表
转义形式 | 意义 | 码位 (DEC) |
---|---|---|
\a | 响铃(BEL) | 7 |
\b | 退格(BS) | 8 |
\f | 换页(FF) | 12 |
\n | 换行(LF) | 10 |
\r | 回车(CR) | 13 |
\t | 水平制表(HT) | 9 |
\v | 垂直制表(VT) | 11 |
\\ | 反斜线字符 \ | 92 |
\' | 单引号(撇号)字符 | 39 |
\" | 双引号字符 | 34 |
\? | 问号 | 63 |
\0 | 空字符(NULL) | 0 |
\DDD | 3 位八进制的对应字符,如 \101 表示 A | / |
\xHH | 2 位十六进制的对应字符,如 \x41 表示 A | / |
长度:strlen
运算符
- 单目、双目与三目
- 栈
经典问题:自增自减与赋值
#include <iostream>
using namespace std;
int main() {
int i = 3;
i = i++;
cout << i << endl;
return 0;
}
++
优先级高于=
,先计算++
- 后缀
++
,将i
的原值 3 保留到某个中间变量中 - 接下来,因为不同的编译器(例如 VS 系列和 GCC 系列
可能有两种执行顺序) , - VS
- 先将原值赋给
i
- 再进行
++
,值为 4
- 先将原值赋给
- GCC
- 先进行
++
,值为 4 - 再将原值赋给
i
,值为 3
- 先进行
- VS
渣哥:无聊的做法,仅为了理解。且不主张对同一个变量的多个 ++
/--
出现在 同一个表达式中
结合
右结合的算符:赋值算符(=
、+=
等)
类型转换
强制类型转换
(类型名)(表达式) C 形式 | 类型名(表达式) C++ 形式 | static_cast<类型名>(表达式) C++ 形式 |
---|---|---|
(int)(a+b) | int(a+b) | static_cast<int>(a+b) |
(int)a | int(a) | static_cast<int>(a) |
- 强制转换后,原变量的值、类型不变(强制转换的结果放在一个中间变量中)
- 若赋值运算符的左右类型不同,则以左值类型为准进行转换
float
/double
char
/short
/int
/long
/long long
时,取整char
/short
/int
/long
/long long
float
/double
时,不会溢出,但精度可能受影响
整型提升
在表达式计算时,各种整形至少要提升为 int
类型,如果 int
类型不足以表示的话,要提升到能表示的类型(包括无符号的
因此有
char ch = 'a';
cout << typeid(+ch).name() << endl;
输出 int
类型。
整型提升不属于强制类型转换。