初步认知#
需求
对于 C++ 代码形成初步认知。
信息的最小单位是 位 (也可称作 比特),一个位仅仅存储 0
或者 1
两个数字。大多数计算机中的基本寻址单元为 字节(1 byte = 8 bit),与一个字符占据的空间大小相当。C++ 对内存做了抽象,将一块内存抽象为一个 对象。对象的 值 便是其对应的内存空间存储的一组比特值,值所代表的含义和所占据的比特数由其 类型 决定。对象需要一个 名字 以方便使用,称给定类型的命名对象为 变量。声明 负责为程序引入一个新的名字,并指定该命名实体的类型。
C++ 提供了若干基本类型,例如:
bool
:布尔类型。可取true
或false
。char
:字符类型。如'a'
,'\n'
等int
:整数类型。如 1,42 等double
:双精度浮点数类型。如 2.3,5.0
这些基本类型都与硬件特性直接相关,其尺寸固定不变,决定了其中所能存储的值的范围。
标识符#
标识符 是以字母和下划线开头,后接 0 个或者多个(大写或者小写)字母、数字或者下划线的序列。标识符也可以称作 名字 或者是 变量名称。
想要使用一个变量,需要使用 声明:
int 苹果数量;
其中 苹果数量
便是一个变量;int
便是一个类型。
int hi_world;
int HiWorld;
int Hi World; // 错误:因为标识符不能有空格
input_line_9:2:8: error: expected ';' at end of declaration
int Hi World; // 错误:因为标识符不能有空格
^
;
Interpreter Error:
备注
C++ 使用符号 //
注解代码片段,以帮助人们理解。编译器不会编译注解内容。
int Hi@World; // 错误:因为标识符不能有 @
input_line_10:2:8: error: expected ';' at end of declaration
int Hi@World; // 错误:因为标识符不能有 @
^
;
Interpreter Error:
双下划线#
以下划线开头或者包含连续两个下划线的标识符是为 C++ 实现预留的,你的程序中不要使用这种标识符。例如:
int _foo; // 不要这样用
int foo_boor; // 正确
int foo__bar; // 不要这样用
int foo_; // 正确
关键字#
关键字 是 C++ 用来表示语言结构的特殊标识符。比如,类型 int
便是关键字。
基本运算符#
运算符可以用于操作对象以实现某些功能。下面仅仅介绍一些基本运算符:
- 算术运算符
x+y // 加法 +x // 一元加法 x-y // 减法 -x // 一元减法 x*y // 乘法 x/y // 除法 x%y // 取模(x,y 不能是浮点数类型)
- 比较运算符
x==y // 相等 x!=y // 不相等 x<y // 小于 x>y // 大于 x<=y // 小于等于 x>=y // 大于等于
- 赋值运算符
=
为一个变量赋予新的值。
运算符 sizeof
#
可以使用运算符 sizeof
获取类型的对象大小。
sizeof(bool) // 1 个字节
1
sizeof(char) // 1 个字节
1
sizeof(int) // 4 个字节
4
sizeof(double) // 8 个字节
8
初始化#
通用初始化方法是使用花括号内的一组初始化器列表:
double d1 {2.3};
d1
2.3000000
当然也可以使用传统的方法,即赋值语句:
double d1 = 2.3;
d1
2.3000000
赋值语句#
看几个赋值语句的例子:
int a = 7; // a 初始化为 7
a
7
a = 4; // a 通过 赋值语句,获得新值 4
a
4
int b = a; // b 的值为 a 的值的拷贝
b
4
b = a + 5; // b 获得新值 a+5
b
9
a = a + 7; // a 获得新值 a+7
a
11
复合赋值运算符#
一个变量的递加(增加 1),可以简写为 ++vars
。比如:
int a = 7;
a // 查看 a 的值
7
++a; // 等同于 `a = a + 1;`
a
8
--a; // 等同于 `a = a - 1;`
a
7
一些二元赋值运算符,可以直接修改变量的当前值:
int a = 7;
a += 7; // 表示 `a = a + 7;`
a
14
a -= 7; // 表示 `a = a - 7;`
a
7
a *= 2; // 表示 `a = a * 2;`
a
14
a /= 2; // 表示 `a = a / 2;`
a
7
a %= 2; // 表示 `a = a % 2;`
a
1
在很多领域 *=
和 /=
被视为“缩放”。
表达式#
表达式是从一些 操作数 计算一个值。最简单的表达式是字面常量,例如:10
、'a'
、3.14
、"喜爱"
。
变量也是一种表达式,它表示与名字对应的那个对象。同样赋值语句也是表达式。
// 计算一些表达式
int 长度 = 30; // 整数字面常量(用于变量的初始化)
int 宽度 = 50;
int 面积 = 长度 * 宽度; // 乘法运算
备注
字面常量
30
和50
用于初始化变量长度
和宽度
;长度
和宽度
进行乘法运算,此时可以将它们理解为变量的值进行乘法运算。
重要
名字在赋值号左边称为 左值,名字在赋值号右边称为 右值。
赋值语句 int 长度 = 30;
,=
左边的名字其含义是名字为 长度
的变量,此赋值语句的含义是“把 30
赋值给名为 长度
的变量”;赋值语句 int 面积 = 长度 * 宽度;
右边的名字 长度
表示这个变量的值。
sizeof
也可以计算表达式的 结果对象 的大小。
float x = 56;
sizeof(x*5+10) // 计算的是 x*5+10 的结果 290.0 的大小
4
备注
x % y
表达式中的 x
与 y
不能是浮点型。
double x {45};
double y {7};
x % y
input_line_55:4:3: error: invalid operands to binary expression ('double' and 'double')
x % y
~ ^ ~
Interpreter Error:
double x {45};
int y {7};
x % y
input_line_56:4:3: error: invalid operands to binary expression ('double' and 'double')
x % y
~ ^ ~
Interpreter Error:
int x {45};
int y {7};
x % y
3
对于比较运算符 ==
避免出现歧义,单独使用时,请使用 ()
括起来。
x == y; // 这种写法有歧义
input_line_61:2:4: note: use '=' to turn this equality comparison into an assignment
x == y; // 这种写法有歧义
^~
=
false
(x == y); // 这样才是正确的写法
false
类型安全#
对于一个程序或者程序的一部分,若使用的对象符合它们的规定,则它们是类型安全的。
下面列出一些不是类型安全的程序:
double xxx; // 仅仅声明,没有初始化,是未定义的
double y = xxx; // y 的值也是未定义的
double z = 2.0 + xxx; // + 的意义和 z 都是未定义的
建议
永远记住初始化你的变量,是一个好习惯!
安全转换#
C++ 提供了显式的类型转换:
char c = '!';
x
45
int t = c; // 安全转换为 int
t
33
int t2 = '!';
t2
33
这里 t
与 t2
都被赋值 33
。我们称这种 char-int 的转换为安全的,这是因为没有信息丢失;同样,可以将 int
的结果拷贝会一个 char
中,并且得到原始值:
char c2 = t;
c2
'!'
不安全转换#
不安全指的是一个值可以转换为一个不等于原始值的值。
int a = 525;
a
525
char c = a; // 试图将一个“大”的整数“压缩”进小的字符型
c
'\r'
int b = c;
b
13
可以看出 a
不等于 b
。
这种转换又称为“窄化”转换。
类型转换#
赋值运算、算术运算以及初始化会在基本类型间进行有意义的类型转换。
重要
若算术表达式中有 double
类型数据,则进行浮点型算术运算,结果为 double
类型;否则就使用整型算术运算,结果为 int
类型。
5/2 // 整型算术运算
2
2.5/2 // 浮点型算术运算
1.2500000
重要
记号 type(value)
和 type{value}
表示“将 value
转换为 type
类型,就像用值 value
初始化 type
类型的变量一样。”
'a' + 1; // 等同于 int{'a'} + 1
98
double d = 2.5; // 初始化浮点数
d
2.5000000
int t = 2; // 初始化整数
t
2
double d2 = d / t;
d2
1.2500000
int t2 = d / t; // 窄化转换
t2
1
d / t
是一个 double
值,而 t2
是 int
,所以最终的结果被截断为整数。
重要
使用 type{value}
可以避免窄化转换,而 type(value)
不能。
int t3 {d / t};
input_line_94:2:10: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
int t3 {d / t};
^~~~~
input_line_94:2:10: note: insert an explicit cast to silence this issue
int t3 {d / t};
^~~~~
static_cast<int>( )
Interpreter Error: