- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本 - 添加 C++ Primer 练习代码 - 新增 Learn C++ 教程的 C++ 开发简介章节 - 添加头文件解析文档 - 更新 mkdocs.yml,包含新教程的目录结构 - 修改项目设置,使用 Python 3.10环境
249 lines
11 KiB
Markdown
249 lines
11 KiB
Markdown
---
|
||
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 = π`。
|
||
- **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
|
||
``` |