---
sort: 2
---

# 变量和基本类型

### 基本内置类型

**基本算数类型**：

| 类型          | 含义           | 最小尺寸                       |
| ------------- | -------------- | ------------------------------ |
| `bool`        | 布尔类型       | 8bits                          |
| `char`        | 字符           | 8bits                          |
| `wchar_t`     | 宽字符         | 16bits                         |
| `char16_t`    | Unicode字符    | 16bits                         |
| `char32_t`    | Unicode字符    | 32bits                         |
| `short`       | 短整型         | 16bits                         |
| `int`         | 整型           | 16bits (在32位机器中是32bits)  |
| `long`        | 长整型         | 32bits                         |
| `long long`   | 长整型         | 64bits （是在C++11中新定义的） |
| `float`       | 单精度浮点数   | 6位有效数字                    |
| `double`      | 双精度浮点数   | 10位有效数字                   |
| `long double` | 扩展精度浮点数 | 10位有效数字                   |


### 如何选择类型

- 1.当明确知晓数值不可能是负数时，选用无符号类型；
- 2.使用`int`执行整数运算。一般`long`的大小和`int`一样，而`short`常常显得太小。除非超过了`int`的范围，选择`long long`。
- 3.算术表达式中不要使用`char`或`bool`。
- 4.浮点运算选用`double`。

### 类型转换

- 非布尔型赋给布尔型，初始值为0则结果为false，否则为true。
- 布尔型赋给非布尔型，初始值为false结果为0，初始值为true结果为1。

### 字面值常量

- 一个形如`42`的值被称作**字面值常量**（literal）。
  - 整型和浮点型字面值。
  - 字符和字符串字面值。
    - 使用空格连接，继承自C。
    - 字符字面值：单引号， `'a'`
    - 字符串字面值：双引号， `"Hello World""`
    - 分多行书写字符串。
      ```
      std:cout<<"wow, a really, really long string"
                "literal that spans two lines" <<std::endl;
      ```
  - 转义序列。`\n`、`\t`等。
  - 布尔字面值。`true`，`false`。
  - 指针字面值。`nullptr`

> 字符串型实际上时常量字符构成的数组，结尾处以`'\0'`结束，所以字符串类型实际上长度比内容多1。

## 变量

**变量**提供一个**具名**的、可供程序操作的存储空间。   `C++`中**变量**和**对象**一般可以互换使用。

### 变量定义（define）

- **定义形式**：类型说明符（type specifier） + 一个或多个变量名组成的列表。如`int sum = 0, value, units_sold = 0;`
- **初始化**（initialize）：对象在创建时获得了一个特定的值。
  - **初始化不是赋值！**：
  - 初始化 = 创建变量 + 赋予初始值
  - 赋值 = 擦除对象的当前值 + 用新值代替
  - **列表初始化**：使用花括号`{}`，如`int units_sold{0};`
  - 默认初始化：定义时没有指定初始值会被默认初始化；**在函数体内部的内置类型变量将不会被初始化**。
  - 建议初始化每一个内置类型的变量。

### 变量的**声明**（declaration） vs **定义**（define）
  - 为了支持分离式编译，`C++`将声明和定义区分开。**声明**使得名字为程序所知。**定义**负责创建与名字关联的实体。
  - **extern**：只是说明变量定义在其他地方。
  - 只声明而不定义： 在变量名前添加关键字 `extern`，如`extern int i;`。但如果包含了初始值，就变成了定义：`extern double pi = 3.14;`
  - 变量只能被定义一次，但是可以多次声明。定义只出现在一个文件中，其他文件使用该变量时需要对其声明。
- 名字的**作用域**（namescope）`{}`
  - **第一次使用变量时再定义它**。
  - 嵌套的作用域
    - 同时存在全局和局部变量时，已定义局部变量的作用域中可用`::reused`显式访问全局变量reused。
    - **但是用到全局变量时，尽量不适用重名的局部变量。**

#### 变量命名规范
1. 需体现实际意义
2. 变量名用小写字母
3. 自定义类名用大写字母开头：Sales_item
4. 标识符由多个单词组成，中间须有明确区分：student_loan或studentLoan，不要用studentloan。

## 左值和右值

- **左值**（l-value）**可以**出现在赋值语句的左边或者右边，比如变量；
- **右值**（r-value）**只能**出现在赋值语句的右边，比如常量。


## 复合类型

### 引用

> 一般说的引用是指的左值引用
- **引用**：引用是一个对象的别名，引用类型引用（refer to）另外一种类型。如`int &refVal = val;`。
- 引用必须初始化。
- 引用和它的初始值是**绑定bind**在一起的，而**不是拷贝**。一旦定义就不能更改绑定为其他的对象

### 指针

> int *p;      //**指向int型对象**的指针

- 是一种 `"指向（point to）"`另外一种类型的复合类型。

- **定义**指针类型： `int *ip1;`，**从右向左读有助于阅读**，`ip1`是指向`int`类型的指针。

- 指针存放某个对象的**地址**。

- 获取对象的地址： `int i=42; int *p = &i;`。 `&`是**取地址符**。

- 指针的类型与所指向的对象类型必须一致（均为同一类型int、double等）

- 指针的值的四种状态：
  - 1.指向一个对象；
  - 2.指向紧邻对象的下一个位置；
  - 3.空指针；
  - 4.无效指针。
  - >**对无效指针的操作均会引发错误，第二种和第三种虽为有效的，但理论上是不被允许的**
  
- 指针访问对象： `cout << *p;`输出p指针所指对象的数据， `*`是**解引用符**。
- 像 `&` 和 `*`这样的符号，既能用作表达式里的运算符，也能作为声明的一部分出现，符号的上下文决定了符号的意义。
```cpp
int i = 42;
int &r = i;   // & 紧随类型名出现，因此是声明的一部分，r是一个引用
int *p;       // * 紧随类型名出现，因此是声明的一部分，p是一个指针
p = &i;       // & 出现在表达式中，是一个取地址符
*p = i;       // * 出现在表达式中，是一个解引用符
int &r2 = *p; // &是声明的一部分，*是一个解引用符
```



- 空指针不指向任何对象。使用`int *p=nullptr;`来使用空指针。

- > 指针和引用的区别：引用本身并非一个对象，引用定义后就不能绑定到其他的对象了；指针并没有此限制，相当于变量一样使用。

- > 赋值语句永远改变的是**左侧**的对象。

- `void*`指针可以存放**任意**对象的地址。因无类型，仅操作内存空间，对所存对象无法访问。

- 其他指针类型必须要与所指对象**严格匹配**。

- 两个指针相减的类型是`ptrdiff_t`。

- 建议：初始化所有指针。

- `int* p1, p2;//*是对p1的修饰，所以p2还是int型`

## const限定符

- 动机：希望定义一些不能被改变值的变量。

### 初始化和const
- `const` 对象**必须初始化**，且**不能被改变**。
- `const` 变量默认不能被其他文件访问，非要访问，必须在指定 `const` 定义之前加 `extern` 。要想在多个文件中使用const变量共享，定义和声明都加 `extern` 关键字即可。

### const的引用

- **reference to const**（对常量的引用）：指向const对象的引用，如 `const int ival=1; const int &refVal = ival;`，可以读取但不能修改`refVal`。
- **临时量**（temporary）对象：当编译器需要一个空间来暂存表达式的求值结果时，临时创建的一个未命名的对象。
- 对临时量的引用是非法行为。

### 指针和const

- **pointer to const**（指向常量的指针）：不能用于改变其所指对象的值, 如 `const double pi = 3.14; const double *cptr = &pi;`。
- **const pointer**：指针本身是常量，也就是说指针固定指向该对象，（存放在指针中的地址不变，地址所对应的那个对象值可以修改）如 `int i = 0; int *const ptr = &i;`

### 顶层const

- `顶层const`：指针本身是个常量。
- `底层const`：指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。

### `constexpr`和常量表达式（▲可选）

- 常量表达式：指值不会改变，且在编译过程中就能得到计算结果的表达式。
- `C++11`新标准规定，允许将变量声明为`constexpr`类型以便由编译器来验证变量的值是否是一个常量的表达式。

## 处理类型

### 类型别名

- 传统别名：使用**typedef**来定义类型的同义词。 `typedef double wages;`
- 新标准别名：别名声明（alias declaration）： `using SI = Sales_item;`（C++11）

```cpp
// 对于复合类型（指针等）不能代回原式来进行理解
typedef char *pstring;  // pstring是char*的别名
const pstring cstr = 0; // 指向char的常量指针
// 如改写为const char *cstr = 0;不正确，为指向const char的指针

// 辅助理解（可代回后加括号）
// const pstring cstr = 0;代回后const (char *) cstr = 0;
// const char *cstr = 0;即为(const char *) cstr = 0;
```

### auto类型说明符 c++11

- **auto**类型说明符：让编译器**自动推断类型**。
- 一条声明语句只能有一个数据类型，所以一个auto声明多个变量时只能相同的变量类型(包括复杂类型&和*)。`auto sz = 0, pi =3.14//错误`
- `int i = 0, &r = i; auto a = r;` 推断`a`的类型是`int`。
- 会忽略`顶层const`。
- `const int ci = 1; const auto f = ci;`推断类型是`int`，如果希望是顶层const需要自己加`const`

### decltype类型指示符

- 从表达式的类型推断出要定义的变量的类型。
- **decltype**：选择并返回操作数的**数据类型**。
- `decltype(f()) sum = x;` 推断`sum`的类型是函数`f`的返回类型。
- 不会忽略`顶层const`。
- 如果对变量加括号，编译器会将其认为是一个表达式，如int i-->(i),则decltype((i))得到结果为int&引用。
- 赋值是会产生引用的一类典型表达式，引用的类型就是左值的类型。也就是说，如果 i 是 int，则表达式 i=x 的类型是 int&。
- decltype((variable))(双层括号)的结果永远是引用，而decltype(variable)的结果只有当 **variable** 本身是一个引用时才是引用

## 自定义数据结构

### struct

> 尽量不要把类定义和对象定义放在一起。如`struct Student{} xiaoming,xiaofang;`
- 类可以以关键字`struct`开始，紧跟类名和类体。
- 类数据成员：类体定义类的成员。
- `C++11`：可以为类数据成员提供一个**类内初始值**（in-class initializer）。

### 编写自己的头文件

- 头文件通常包含哪些只能被定义一次的实体：类、`const`和`constexpr`变量。

预处理器概述：

- **预处理器**（preprocessor）：确保头文件多次包含仍能安全工作。
- 当预处理器看到`#include`标记时，会用指定的头文件内容代替`#include`
- **头文件保护符**（header guard）：头文件保护符依赖于预处理变量的状态：已定义和未定义。
  - `#indef`已定义时为真
  - `#inndef`未定义时为真
  - 头文件保护符的名称需要唯一，且保持全部大写。养成良好习惯，不论是否该头文件被包含，要加保护符。

```cpp
#ifndef SALES_DATA_H  //SALES_DATA_H未定义时为真
#define SALES_DATA_H
strct Sale_data{
    ...
}
#endif
```