技术交流

好好学习,天天向上。

0%

第二章——变量和基本类型

第二章——变量和基本类型

2.1 基本内置类型

2.1.1 算数类型

类型 含义 最小尺寸
bool boolean NA
char character 8 bits
wchar_t wide character 16 bits
char16_t Unicode character 16 bits
char32_t Unicode character 32 bits
short short integer 16 bits
int integer 16 bits
long long integer 32 bits
long long long long integer 64 bits
float single-precision floating-point 6 significant digits
double double-precision floating-point 10 significant digits
long double extended-precision floating-point 10 significant digits

编码时应当遵循以下选型策略:

  • 明确知道数值不可能为负,选用无符号类型
  • 一般形况下选用int进行整形运算。实际使用时,short往往太小而long又经常和int同样长度。所以当int不足以容纳数值时,可以直接选用long long类型
  • 算数表达式中,不要用char。因为char是否为有符号类型在不同的机型/编译器上可能存在差异。如果确实需要一个很小的整形,建议指明signed char或者unsigned char。
  • 不要用bool做算数运算,尽管bool可以隐式转换成整形,但是隐式类型转换本身就存在风险
  • 执行浮点运算首选double,因为float的精度没有double高,但是在如今的计算机上,其运算效率上和double相差不大甚至更慢。long double一般不用,因为这种类型很可能没有CPU的原生指令支持,性能开销可能过大

2.1.2 类型转换

需牢记,考虑代码的可移植性,编码时不要假定这些隐式类型转换规则一定存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;
int main() {
bool a, b;
a = 3.14;
b = 0;
cout << boolalpha << "a = " << a << " " << "b = " << b << endl; // 非零值转bool为true,零值转bool为false

int c = a;
int d = b;
cout << boolalpha << "c = " << c << " " << "d = " << d << endl; // bool转数值类型时,true转换后得到1,false转换后得到0

int e = 3.54;
cout << "e = " << e << endl; // 浮点转换为整型的时候,会抹掉小数部分,没有四舍五入

/*
* 实际使用时,有符号无符号、不同位宽的类型之间相互赋值遵循以下原则
* 宽类型赋值给窄类型,将截去宽类型的高位,然后将与窄类型相同长的低位部分赋值给窄类型,被赋值类型的符号类型不受影响
* 窄类型赋值给宽类型,将用0扩展窄类型的高位直至和宽类型等长,然后将最终结果赋值给宽类型,被赋值类型的符号类型不受影响
*/
}

隐式类型转换不仅存在于赋值过程,还存在于表达式的计算过程。由此引申出以下几个需要注意的点:

  • 有符号类型和无符号类型不要在表达式中混用
  • 注意运算过程的溢出

2.1.3 字面值常量

  • 默认情况下,十进制字面值常量是带符号数。八进制和十六进制数是否带符号不确定
  • 虽然默认情况下,十进制字面值常量是带符号数,但字面值不会是负数。“-42”这种表达式中“-”号不是字面值常量的一部分
  • 十进制字面值的类型是int、long和long long中可容纳该字面值的类型中尺寸最小的那个
  • short类型没有对应的字面值
  • 编译器会自动在字符串字面值后面添加空字符“\0”,多个相邻的字符串
  • 如果两个字符串字面值位置相邻,由空格、tab或者换行分割,则这两个字符串是一个整体
1
2
3
4
5
6
7
#include <iostream>

using namespace std;
int main() {
cout << "123" "456"; // 输出123456
return 0;
}
  • 可以通过添加前缀或者后缀来给字面值常量指定类型

image-20220405200649343

2.2 变量

2.2.1 变量定义

什么是变量

这本书对变量的定义为,一块能存储数据并具有某种类型的内存空间,并不严格区分是类还是内置类型, 也不区分是否命名或是否只读。

初始值

在C++中,初始化和赋值,是两个完全不同的操作。初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,然后用以一个新值未替代。

列表初始化

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;
int main() {
int a = 0;
int b = {0}; // C++有复杂的初始化语法,变量b和c这种用花括号初始化的语法称为“列表初始化”
int c{0}; // 列表初始化的好处是统一了内置类型和类类型的初始化风格
int d(0);

cout << a << b << c << d;
return 0;
}

列表初始化和其他初始化语法的一个重要不同是,如果编译器发现列表初始化动作会导致信息丢失,那么编译器会报错。

image-20220405202734718

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main() {

int a = {3.14}; // 无法编译通过
int b = 3.14; // 可以编译通过
double c = 3.14;
int d{c}; // 但是如果初始值是一个变量,则编译器就只会给出警告,这里和书上有点出入

return 0;
}

默认初始化

内置类型的默认初始化值和链接行为有关,建议直接看链接这篇文章。类对象的默认初始化行为由默认构造函数决定。

2.2.2 变量声明和定义的关系

和符号的强弱属性有关,仍然建议直接看链接这篇文章。

2.2.3 标识符

C++的标识符(identifier) 由字母、数字和下画线组成,编码时命名规则需遵循所在团队的编码规范

2.2.4 名字的作用域

典型的作用域:

  • 全局作用域:类似于main函数所在区域、在所有花括号之外的作用域就是全局作用域

  • 块作用域:一对花括号就可以创建一个块作用域,例如循环体,函数体等

  • 命名作用域:编码人员可以自行创建带有名称的作用域

  • 类作用域:属于一个类的作用域

嵌套的作用域

作用域可以嵌套,内层作用域可以用同名变量覆盖外层作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
int main() {

int a = 0, b = 0; // 外层的a
for (int i = 0; i < 10; i++) { // 外层的i
int a = 0; // 外层的b
for (int i = 0; i < 10; i++) { // 内存的i
a++;
b++;
}
}
cout << "a = " << a << ", " << "b = " << b << endl;
return 0;
}

2.3 复合类型

2.3.1 引用

不强调的情况下,引用一般指定时左值引用,引用即别名

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>
using namespace std;
int main() {

string var_a = "var_b 就是 var_a\n";
string &var_b =var_a;
var_b = "var_a 就是 var_b\n";
cout << var_a;
return 0;
}

引用的定义

1
int &r = i , r2 = i2; // 只有r是引用,r2是int变量