--- 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" < 字符串型实际上时常量字符构成的数组,结尾处以`'\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 ```