初步认知#

需求

对于 C++ 代码形成初步认知。

信息的最小单位是 (也可称作 比特),一个位仅仅存储 0 或者 1 两个数字。大多数计算机中的基本寻址单元为 字节(1 byte = 8 bit),与一个字符占据的空间大小相当。C++ 对内存做了抽象,将一块内存抽象为一个 对象。对象的 便是其对应的内存空间存储的一组比特值,值所代表的含义和所占据的比特数由其 类型 决定。对象需要一个 名字 以方便使用,称给定类型的命名对象为 变量声明 负责为程序引入一个新的名字,并指定该命名实体的类型。

C++ 提供了若干基本类型,例如:

  • bool:布尔类型。可取 truefalse

  • 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 面积 = 长度 * 宽度; // 乘法运算

备注

  • 字面常量 3050 用于初始化变量 长度宽度

  • 长度宽度 进行乘法运算,此时可以将它们理解为变量的值进行乘法运算。

重要

名字在赋值号左边称为 左值,名字在赋值号右边称为 右值

赋值语句 int 长度 = 30;= 左边的名字其含义是名字为 长度 的变量,此赋值语句的含义是“把 30 赋值给名为 长度 的变量”;赋值语句 int 面积 = 长度 * 宽度; 右边的名字 长度 表示这个变量的值。

sizeof 也可以计算表达式的 结果对象 的大小。

float x = 56;
sizeof(x*5+10) // 计算的是 x*5+10 的结果 290.0 的大小
4

备注

x % y 表达式中的 xy 不能是浮点型。

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

这里 tt2 都被赋值 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 值,而 t2int,所以最终的结果被截断为整数。

重要

使用 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: