环境搭建以及《C++ Primer》第一、二章
1. 环境搭建
参考网址:
https://www.cnblogs.com/cpuimage/p/13121765.html
(1) 下载msys2
https://www.msys2.org/
(2) 打开msys2控制台,输入以下命令进行环境安装
pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-gdb
pacman -S mingw-w64-x86_64-cmake
pacman -S mingw-w64-x86_64-make
pacman -S mingw-w64-x86_64-pkg-config
(3) 下载并打开 CLion,进入setting中的Toolchains;点击 [+] 新建MinGW环境并配置,如下图
2. C++基础
(1) 相关术语
- 形参:
parameter
,formal argument - 实参:
argument
, actual argument - 编译器:
gcc
(GUN Compiler Collection), 预处理、编译、汇编、链接 - 编辑器:
- 集成开发环境:
IDE
(Integrated Developed Environment) - 语句块:
block
,花括号包围的语句序列
(2) 向流写入数据
#include <iostream>
std::count << "Enter two numbers:" << std::endl;
std::cin >> v1 >> v2; // 从std::cin中读入两个值,第一个存入v1,第二个存入v2
endl
操纵符(manipulator),结束当前行,并将缓冲区(buffer)中的内容刷到设备中;std
命名空间(namespace)::
作用域运算符//
单行注释/* * */
注释界定符
(3) 以 istream
对象作为条件的循环
#include <iostream>
int main() {
int sum = 0, value = 0;
while (std::cin >> value)
sum += value;
std::cout << "Sum is: " << std::endl;
return 0;
}
- 如果输入的流是有效的(在该代码中为
int
型参数),则检测成功,条件为True
; - 如果输入的流是无效的(除
int
型参数外其他类型参数),则检测失败,条件为False
,跳出循环。
3. 变量和基本类型
(1) 算术类型
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8 位 |
wchar_t | 宽字符 | 16 位 |
char16_t | Unicode 字符 | 16 位 |
char32_t | Unicode 字符 | 32 位 |
short | 短整型 | 16 位 |
int | 整形 | 16 位 |
long | 长整型 | 32 位 |
long long | 长整型 | 64 位 |
float | 单精度浮点数 | 6 位有效数字 |
double | 双精度浮点数 | 10 位有效数字 |
long double | 扩展精度浮点数 | 10 位有效数字 |
- 字节:8 比特
- 字: 32 或 64 比特
float
一个字(32 比特)
(2) 类型转换
-
把非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true;
-
把浮点数赋给整数类型时,结果仅保留小数点前的部分;
-
表达式中既有负数又有无符号数时,会出现异常结果
(3) 字面值常量
-
以
0
开头的整数代表八进制,以0x
或0X
开头的代表十六进制数,例如,可以用以下几种形式表示20:20 // 十进制
024 // 八进制
0x14 // 十六进制
-
单引号括起来的一个字符成为
char
型字面值;双引号括起来的成为字符串
型字面值,其结尾位为'\0'
-
一个字符串字面值可以被空格、缩进、换行符分隔
std::cout << "a really, really long string literal " "that spans two lines" << std::endl;
-
转义序列
escape sequence
符号名称 符号表示 符号名称 符号表示 符号名称 符号表示 换行符 \n
横向制表符 \t
报警(响铃)符 \a
纵向制表符 \v
退格符 \b
双引号 \"
反斜线 \\
问号 \?
单引号 \'
回车符 \r
进纸符 \f
-
\7
响铃\12
换行\40
空格\0
空字\115
字符M\x4d
字符M (\
后面为八进制或者十六进制)
(4) 变量
-
初始化
创建变量是赋予一个初始值 -
赋值
擦除对象当前值,并用一个新值替代之 -
声明 (declaration)
使文件可以使用别处定义的名字,变量可以被多次声明 -
定义 definition
创建于名字关联的实体,变量只能被定义一次extern int i; // 声明 extern double pi = 3.1416; // 定义 int j; // 定义
-
变量的
作用域 scope
全局作用域 global scope
在整个程序范围内都可被使用块作用域 block scope
在某一语句或者函数内可被使用
(5) 复合类型 compound type
-
引用 reference
并非对象,它只是为一个已经存在的对象所起的另外一个名字int ival = 1024; int &refVal = ival; // refVal 指向ival(是ival的另一个名字) int &refVal2; // 报错:引用必须被初始化 int &refVal3 = refVal // 正确 int i = 1024, i2 = 2048; int &r = i, r2 = i2; // 正确:允许在一条语句中定义多个引用 int &refVal = 10; // 错误:引用类型的初始值必须是一个对象
-
指针 pointer
存放某个对象的地址- 是对象,允许对指针赋值和拷贝
- 无须在定义时赋初值
- 不能定义指向
引用
的指针
int ival = 42; int *p = &ival; // p存放变量ival的地址,指向变量ival *p = 0; // * 解引用符, ival的值被改变,而指针p并没有改变 double dval; double *pd = &dval; // 正确:pd存放dval的地址 double *pd2 = pd; // 正确:pd2与pd一样,均存放dval的地址
-
空指针 null pointer
不指向任何对象int *p1 = nullptr; // 等价于 int *p1 = 0, 最好用这种方法 int *p2 = 0; // 直接将 p2 初始化为字面常量0 #include cstdlib int *p3 = NULL; // 等价于 int *p3 = 0, 尽量避免使用
(6) const
限定符
-
const
对象必须初始化 -
在多个文件之间共享
const
对象extern const int bufSize = 512; // 定义并初始化一个常量,可以被其他文件访问
-
const
的引用const int ci =1024; const int &r1 = ci; // 正确:引用机器对象都是常量 r1 = 42; // 错误:r1 是对常量的引用,不能修改 int &r2 = ci; // 错误: 试图让一个非常量 引用 指向一个常量对象 int i = 42; const int &r3 = i; // 正确:允许将 const int & 绑定到普通 int 对象上 r3 = 0; // 错误: r3 为常量引用,不能修改其绑定的值 const int &r4 = 42; // 正确:r4 为常量引用 const int &r5 = r3 * 2; // 正确:r5 为常量引用 int &r4 = r3 * 2; // 错误:r4 不是常量引用
-
临时量 temporary
double dval = 3.14; const int &ri = dval; // 非法 // 上一条语句等价于 const int temp = dval; // 由双精度浮点数生成一个临时的整型常量 const int &ri = temp; // 让 ri 绑定此临时量
-
指向
const
的指针const double pi = 3.14; // 定义双精度浮点型常量 pi double *ptr = π // 错误:ptr 是普通指针 const double *cptr = π // 正确:cptr 可以指向双精度常量 *cptr = 42; // 错误:不能给 *cptr 赋值 double dval = 3.14; // dval 是双精度浮点数,其值可以改变 cptr = &dval; // 正确:但是不能通过 cptr 改变 dval 的值
-
const pointer
:常量指针,把指针本身定为常量有点难理解int errNumb = 0; int *const curErr = &errNumb; // curErr 将一直指向 errNumb const double pi = 3.14159; const double *const pip = π // pip 是一个指向常量对象的常量指针
curErr
从右向左阅读:- 首先,
curErr
是一个常量对象, - 其次,
curErr
是一个整型指针, - 因此,
cueErr
是一个指向整型的常量指针,其指向的对象不能改变。
- 首先,
pip
从右向左阅读:- 首先,
pip
是一个常量对象, - 其次,
pip
是一个双精度浮点型指针, - 最后,
pip
指向的是一个常量对象, - 因此,
pip
是一个指向 双精度浮点型常量对象 的 常量指针,其指向的对象不能改变,而且也不能通过*pip
改变其指向对象的值。
- 首先,
-
top-level const
以及low-level const
-
top-level const
顶层 const,表示指针或任意对象本身是一个常量,不能改变指针指向的对象; -
low-level const
底层 const,表示指针(或引用)指向(或绑定)的对象是一个常量,不能通过*point
(或引用)改变指向(或绑定)对象的值,进行 copy 操作时要注意数据类型。int i = 0; int *const p1 = &i; // top-level const const int ci = 42; // top-level const const int *p2 = &ci; // low-level const const int *const p3 = p2; // (low-level const) int (top-level const) const int &r = ci; // low-level const i = ci; // 正确:top-level const 对拷贝操作无影响 int *p = p3; // 错误:p3 包含 low-level const,而 p 没有 p2 = p3; // 正确:p2 为 low-level const p2 = &i; // 正确:int * 能转换成 const int * int &r = ci; // 错误:普通的 int & 不能绑定到 const int 上 const int &r2 = i; // 正确:const int & 可以绑定到普通 int 上
-
-
constexpr
常量表达式:限定符constexpr
仅对指针有效,与指针所指的对象无关
const int *p1 = nullptr; // p1 是一个指向整型常量的指针,low-level
constexpr int *p2 = nullptr; // p2 是一个指向整数的常量指针,top-level
constexpr const int *p3 = nullptr; // p3 是一个指向整型常量的常量指针,(top-level) (low-level)
(7) 类型处理
-
alias declaretion
别名声明有点绕using SI = Sales_item; // 等价于 typedef Sales_item SI; typedef char *pstring; const pstring cstr = 0; // cstr 是指向 char类型对象 的常量指针,而不是指向 char类型常量对象 的指针 const pstring *ps; // ps 是普通指针,它指向的对象是 指向char类型对象的常量指针 const pstring cstr = 0; // const 用于修饰 pstring,说明 cstr 是常量指针,不等价于下一行命令 const char *cstr; // const 用于修饰 char,* 是声明符,说明 cstr 是指向 char类型常量 的指针
-
auto
类型说明符-
auto
一般会忽略top-level const
,保留low-level const
const int ci = 0, &cr = ci; auto b = ci; // b 是一个整数(ci的 top-level const特性被忽略了) auto c = cr; // c 是一个整数(cr的 top-level const特性被忽略了) auto d = &ci; // e 是一个指向 整型常量 的指针(对常量对象取地址是low-level const)
-
如果希望推断出的
auto
类型为top-level const
,需要明确指出const int ci = 0; const auto e = ci; // ci 的推演类型是 int, e 的类型是 const int
-
auto
与引用
设置类型为auto
的引用
时,初始值的top-level const
特性保留;给初始值绑定引用
时,此时的常量就不是top-level const
int i = 0; const int ci = 0; auto &f = ci; // 正确:ci 是整型常量,因此推断的 f 也是常量 auto &g = 42; // 错误:42 不是常量,因此推断的 g 也不是常量,不能为非常量引用 绑定字面值 const auto &h = 42; // 正确:42 不是常量,但是明确指出了 h 是常量,可以为 常量引用 绑定字面值 auto &j = i; // 正确:i 的地址是常量,low-level const (不知道是否可以这样理解?)
-
在一条语句中定义多个变量时,符号
&
和*
只从属于某个声明符,而非基本数据类型的一部分,因此初始值必须为同一类型int i = 0; const int ci = 0; auto k = ci, &l = i; // 正确:k 是整数,l 是整型引用,ci 和 i 的地址都是 const int (??) auto &m = ci, *p = &ci; // 正确:m 是对整型常量的引用,p 是指向整型常量的指针 auto &n = i, *ps = &ci; // 错误:i 的类型是 int,而 &ci 的类型是const int
-
-
decltype 类型指示符
-
decltype
返回变量的类型,包括top-level const
和low-level const
const int ci = 0, &cj = ci; decltype(ci) x = 0; // x 的类型是 const int decltype(cj) y = x; // y 的类型是const int&, y 绑定到变量 x decltype(cj) z; // 错误:z 是一个引用,必须初始化
-
decltype
与引用
int i = 42, *p = &i, &r = i; decltype(r + 0) a; // 正确:表达式的结果类型为 int decltype(r) b; // 错误:r 的类型为 int&,必须初始化 decltype(*p) c; // 错误:*p 的类型为 int&,必须初始化 decltype((i)) d; // 错误:(i) 的类型为 int&,必须初始化 // decltype((variable)) 的结果永远是 引用
-
(8) 自定义数据结构
-
struct
struct Sale_data {};
-
头文件保护
# ifndef SALES_DATA_H // 判断变量是否定义,当且仅当变量为定义时为真 # define SALES_DATA_H // 将 SALES_DATA_HATA_H 设为预处理变量 # include <string> struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; # endif // 当检查 # define 结果为真时,遇到 # endif 指令结束
-
struct
struct Sale_data {};
-
头文件保护
# ifndef SALES_DATA_H // 判断变量是否定义,当且仅当变量为定义时为真 # define SALES_DATA_H // 将 SALES_DATA_HATA_H 设为预处理变量 # include <string> struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; # endif // 当检查 # define 结果为真时,遇到 # endif 指令结束