- 1. Learn_CPP
- 2. 开始学习 C++
- 3. 第三章 处理数据
- 4. 复合类型
- 5. 循环和关系表达式
- 打开 github 仓库地址,直接按下 .,即可打开 githu 的 VSCode 在线打开
- 打开后在运行调试点击运行,可创建 codespace
- 打开根目录下的.vscode/settings.json
- 更改参数,运行根目录下 clean 脚本,即可重新运行
- "cmake.sourceDirectory": "/workspaces/Learn_CPP/4/4_1_arrayone"
- using编译指令
using namespace std;
- using声明
using std::cout;
using std::endl;
using std::cin;
- 只能使用字母字符、数字和下划线(_)
- 名称的第一个字符不可以是数字
- 区分大小写
- 不能使用 C++关键字
- 以两个下划线或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符
- C++名称长度没有限制,但是有些平台有限制
- sizeof(同 C 语言)
- 对类型名使用 sizeof 运算符时,应将类型名放在括号里
- 对变量名使用 sizeof 运算符时,括号是可选的
- 头文件 climits
符号变量 | 表示 |
---|---|
CHAR_BIT | char 的位数 |
CHAR_MAX | char 的最大值 |
CHAR_MIN | char 的最小值 |
SCHAR_MAX | signed char 的最大值 |
SCHAR_MIN | signed char 的最小值 |
UCHAR_MAX | unsigned char 的最大值 |
SHRT_MAX | short 的最大值 |
SHRT_MIN | short 的最小值 |
USHRT_MAX | unsigned short 的最大值 |
INT_MAX | int 的最大值 |
INT_MIN | int 的最小值 |
UINT_MAX | unsigned int 的最大值 |
INT_MAX | int 的最大值 |
INT_MIN | int 的最小值 |
UINT_MAX | unsigned int 的最大值 |
LONG_MAX | long 的最大值 |
LONG_MIN | long 的最小值 |
ULONG_MAX | unsigned long 的最大值 |
LLONG_MAX | long long 的最大值 |
LLONG_MIN | long long 的最小值 |
ULLONG_MAX | unsigned long long 的最大值 |
- 符号常量
- 被设计用来 C 和 C++的头文件必须使用#define
- C++使用关键之 const 方式创建符号常量
- 初始化
- 将变量初始化为字面值常量
- 将变量初始化为另一个变量,条件是后者已经定义过
- 将变量初始化为表达式,条件是当程序执行到该声明时,表达式中的所有值都是已知的。
- C++11 初始化方式
int i = {0};
int i{0}; // 可以不需要等号
int i = {}; // 初始化为0
int i{};
- 有助于更好的防范类型转换错误
- 使新手更容易学习 C++
- 节省一点就是赢得一点
进制 | 说明 |
---|---|
10 进制 | 如果第一位为 1~9 |
8 进制 | 如果第一位为 0,第二位为 1~7 |
16 进制 | 如果前两位为 0x 或 0X。字符 a ~ f 和 A ~ F 表示十六进制位 |
- 输出十六进制和十进制。在修改格式之前,原来格式将一直有效
进制 | 代码 |
---|---|
10 进制 | cout << dec |
8 进制 | cout << oct |
16 进制 | cout << hex |
- 整型常量默认 int
- 后缀:
- u 表示 unsigned
- l 表示 long
- ul 表示 unsigned long
- ll 表示 long long
- 长度:
- 对于不带后缀的十进制,按照 int、long、long long 的顺序查找最长的匹配类型
- 对于不带后缀的八进制和十六进制,按照 unsigned int、unsigned int、unsigned long、unsigned long long 的顺序查找最长的匹配类型
- 值的类型将引导 cout 选择如何显示值
- 成员函数 cout.put()。为什么会有这个成员函数,是历史遗留问题。
- char 字面值
- 单引号
- 转义字符
- 转义序列
- 通用字符名
- 通用字符名以\u 开头,后面是 16 进制数
- 通用字符名可以用于任何字符
- char 默认,既不是没符号,也不是有符号。有需要情况下,需要显示指出。
- wchar_t
- 宽字符类型
- 与 underlying 类型长度和符号属性相同
- cin 和 cout 是 char 流,所有不适于处理 wchar_t
- wcin 和 wcout 处理 wchar_t
- 前缀 L 指示宽字符常量和宽字符串
- char16_t 和 char32_t
- 无符号类型
- u 前缀表示 char16_t
- U 前缀表示 char32_t
- 非 0 即 1
- true 转换成 1,false 转换成 0
- 任何数字值或指针值都可以被隐式转换成 bool(即不用显式强制转换)
- const 声明常量
- const 相较于#define 的优势:
- 明确指定类型
- 使用 C++作用域将定义限制在特定的函数或者文件中
- 可以用于更复杂的类型,如数组和结构
- 数值和缩放因子
- 标准小数表示法(也允许使用,做小数点)
- E 表示法(也可以使用 e) d.ddE+n d.ddE-n d.dde+n -d.ddE+n
-
浮点类型
- float、double、long double
- floa:32 位,指数范围至少-37 到+37
- double:64 位,指数范围至少-37 到+37
- long double:96 位,指数范围至少-37 到+37
- 在头文件 cfloat 或 float.h 中可以找到系统的限制。
- 有些 C++实现未添加头文件 cfloat,有些基于 ANSI C 之前的编译器的 C++实现未添加头文件 float.h。
-
cout.setf()方法
- cout 默认会删除结尾 0,调用 cout.setf()会覆盖这个行为
- 调用 cout.setf(ios_base::fixed, ios_base::floatfield)会保留小数点后的 0
- 调用 cout.setf(ios_base::scientific, ios_base::floatfield)会使用科学计数法
- 默认浮点常量都是 double 类型
- 希望浮点常量是 float 类型,需要在数字后面加上 f 或 F
- 对于 long double 类型,需要在数字后面加上 l 或 L
- 优点
- 可以表示整数之间的值
- 因为有缩放因子,所有可以表示的范围大得多
- 缺点
- 运算速度慢
- 精度将降低
- + 运算符
- - 运算符
- * 运算符
- / 运算符
- % 运算符:
- 求模
- 操作数只能是整数
- 如果操作数其中一个是负数,则满足如下规则:(a/b)*b + a%b = a
- 优先级
- 结合性(方向)
- 两个操作数都是整数,则结果为整数
- 其中一个或者两个为小数,则结果为浮点数
- 求模运算符的结果是整数
- C++自动执行类型转换
- 将一种算术类型的值赋给另一种算术类型的变量时,C++将对值进行转换
- 表达式中包含不同的类型时,C++将对值进行转换;
- 将参数传递给函数时,C++将对值进行转换。
- 初始化和赋值进行的转换
- 以{}方式初始化时进行的转换(C++11)
- 大括号的初始化称为列表初始化
- 列表初始化不允许缩窄
- 且列表初始化需要常量
const int code = 66; int x=66; char c1 {31325}; // 缩窄,不被允许 char c2 = {66}; // 可以,因为存的下 char C3 {code}; // 可以,因为存的下 char C4 = {x}; // 不可以,因为x不是常量 X = 31325; char c5 = x; // 这样的初始化是可以的
- 表达式中的转换
- 出现时自动转换
- 整型提升:
- 在计算表达式时,C++将 bool、char、unsigned char、signed char 和 short 值转换为 int
- 如果 short 比 int 短,则 unsigned short 类型将被转换为 int;如果两种类型的长度相同,则 unsignedshort 类型将被转换为 unsigned int。
- 整型提升:
- 有些类型与其他类型同时出现在表达式时将被转换
- 出现时自动转换
- 传递参数时的转换:传递参数时的类型转换通常由 C++函数原型控制。
- 强制类型转换:
- 强制转换的通用格式如下:
- (typeName) value // 来自 C 语言
- typeName (value) // 纯粹的 C++
- 4 个强制类型转换运算符(15 章介绍) * static_cast<typeName> (value) 可用于将值从一种数值类型转换为另一种数值类型
- 强制转换的通用格式如下:
- 如果使用关键字 auto,而不指定变量的类型,编译器将把变量的类型设置成与初始值相同:
- 数组声明:
- 存储在每个元素中的值的类型;
- 数组名;
- 数组中的元素数。编译时已知
- 数组使用:
- 可以单独访问数组元素。使用下标或者索引。
- 从 0 开始
- 编译器不会检查使用发下标是否有效
- 只有在定义数组时才能使用初始化,此后就不能使用了
- 不能将一个数组赋给另一个数组
- 初始化数组时,提供的值的个数可以少于数组的元素数目。
- 如果只对数组的一部分进行初始化,则编译器将把其他元素设置为 0。
- 如果初始化数组时方括号内([])为空,C++编译器将计算元素个数
列表初始化
- 初始化数组时,可省略等号(=)
- 可不在大括号内包含任何东西,这将把所有元素都设置为零
- 列表初始化禁止缩窄转换
- C 风格字符串
- 以空字符结尾,空字符被写作'\0'
- 字符数组初始化为字符串方法:
- 显式加上空字符
- 使用双引号括起的字符串(字符串常量或字符串字面值)
- 字符串读入 char 数组中时,会自动加上空字符
- 字符串常量不能与字符常量互换
- 基于 string 类库
- 任何两个由空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接成一个。
- 将数组初始化为字符串常量
- 将键盘或文件输入读入到数组中
- 确认字符串长度(只计算可见字符长度)-strlen()
- cin 使用空白(空格、制表符和换行符)来确定字符串的结束位置,且空白符会保存在输入队列
- cin 在获取字符数组输入时,只读取一个单词
- cin 开始输入之前它们跳过空白(空格、换行符和制表符)
- 面向行的输入:cin.getline(字符数组名, 字符个数, 结束标志)
- 字符个数包括不可见字符
- 默认使用换行符作为结束标志
- 不读入输入的字符串的结束标志(如换行符)
- 结束符不会保存在输入队列
- 面向行的输入:cin.get
- cin.get(char* s, streamsize n):会读取到行尾,但是换行符会保存在输入队列
- cin.get():可读取下一个字符(即使是换行符)
- cin.get(char* s, streamsize n).get():消耗掉输入队列的换行符
- 为什么使用 get()而不是 getline()?
- 老式未实现 getline
- get 使输入更仔细。比如如何知道停止读取的原因是已经读取一整行了还是数组填满了?可以使用 cin.get()查看下一个输入字符,如果下一个输入字符是换行符,则说明已经读取了一整行。
- 空行和其他问题
- 空行:
- 最初做法:下一条输入语句将在前一条 getline()或 get()结束读取的位置开始读取
- 当前做法:当 get()(不是 getline())读取空行后将设置时效为(failbit),之后输入将被阻断。使用 cin.clear()来恢复输入。
- 其他问题: * 输入行包含的字符数比指定的多,get()和 getline()将把余下字符留在输入队列,而 getline()会设置失效位,并关闭之后输入。
- 空行:
- 当混合输入数字和字符串时,需注意数字会把换行符留在输入队列中
- C++98 添加
- 需包含头文件 string,string 类位于命名空间 std 中
- string 对象和字符数数组区别:
- 可以将 string 对象声明为简单变量而不是数组
- 类设计可以让程序自动处理string 的大小
- 可以将一个 string 对象赋值给另一个 string 对象,而数组不可以
- 可以使用+合并两个 string 对象,可以使用+=追加
- 可以将 C 风格字符串或 string 对象与 string 对象相加,或将他们附加到 sting 对象的末尾
- 对于 c 风格字符串,使用 cstring 中的函数完成操作
- string 对象相较于 c 风格字符数组优势:
- 处理时语法更简单
- 字符数组总是存在目标数组过小,无法存储指定信息的危险
- string 类具有自动调整大小的功能
- c 风格字符串,提供strncat()和 strncpy(),可以指定目标数组最大允许长度
- 处理 string 对象的代码使用 string 类的一个友元函数
字符类型 | 字面值前缀 | 标准 |
---|---|---|
char | ||
wchar_t | L | |
char16_t | u | C++11 |
char32_t | U | C++11 |
- C++11 新增:
- UTF-8 使用 u8 前缀
- 原始(raw)字符串: _ R 前缀(即不再转义,界定符为 "( 和 )") _ 允许支持自定义界定符: _ 如:**"+(** 和 **)+"** _ 默认界定符之间添加任意数量的基本字符,但空格、括号、斜杠和控制字符除外
- 创建结构:
- 定义结构描述
- 创建结构变量
- C++允许在声明结构体变量时省略关键字 struct
- C++不提倡使用外部变量,但是提倡使用外部结构体声明。另外在外部声明符号常量通常更合理
- 列表初始化,且等号可选
- 若大括号未包含任何东西,每个成员被设置为零
- 不允许缩窄转换
#include <string>
struct inflatable
{
std::string name;
float volume;
double price;
};
- 可以作为函数参数或者返回值
- 可以使用赋值运算符将整个结构体赋值给另一个结构体变量(成员赋值)
- 可以同时完成定义结构体和创建结构体变量
- 可以声明没有名称的结构体类型
- 允许指定占用特定位数的结构体成员
- 字段的类型应该为整型或者枚举,接下来是冒号,冒号后面是一个数字,它指定了使用的位数
- 可以使用没有名称的字段来提供间距
- 每个成员都被称为位字段
struct torgle_register
{
unsigned int SN : 4; // 4 bits for SN value
unsigned int : 4; // 4 bits unused
bool goodIn : 1; // valid input (1 bit)
bool goodTorgle : 1; // successful torgling
}
- 能存储不同数据类型,但是同时只能存储其中一种类型
- 共用体长度为起最大成员的长度
- 创建符号常量
- 代替 const
- 允许定义新类型,但必须按严格的限制进行
- 枚举体,枚举量
- 默认将整数值赋给枚举量,可以显式的指定整数值来覆盖默认值
- 枚举变量具有一些特殊属性:
- 在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋值给这种枚举的变量
- 对于枚举,只定义了赋值运算符,没有定义算术运算符
- 枚举量是整型,可被提升为 int 类型,但 int 不能自动转换成枚举类型
- 如果 int 值有效,则可以通过强制类型转换将 int 值赋给枚举变量
- 可以使用赋值运算符显式地给枚举量赋值
- 指定的值,必须是整数
- 也可以显式地定义其中一些枚举量的值。默认从零开始。后面未定义的值,比前面枚举量大 1。
- 可以创建多个值相同的枚举量
- 上限:
- 最大值的最小 2 次幂减 1。如 101->128->127
- 下限:
- 最小值不小于 0,则下限为 0
- 最小值小于 0,最大值的最小 2 次幂减 1 加上负号。如-6 -> -8 -> -7
- 显示地址时,cout 使用十六进制
- 常规变量:值是指定的量,地址是派生量
- *运算符:间接值或解除引用运算符
- 4_14_address.cpp
- 4_15_pointer.cpp
- * 运算符两边空格可选
- 每个指针变量名都需要使用一个*。如:int *p1, *p2
- int *是一种复合类型
- 指向不同类型的指针变量长度相同
- 在声明时初始化指针,被初始化的是指针而不是它指向的值 4_16_init_ptr.cpp
- 创建指针时,并不会分配用来存储指针所指向数据的内存
- 将数字值作为地址使用,应当通过强制类型转换为适当的地址类型,否则会报错
- 数据对象:为数据项分配的内存块
- 为一个数据对象获得并指定分配内存的通用格式:
typeName * pointer_name = new typeName;
- 必须声明指针所指向的类型:地址本身只指出了对象存储地址的开始,而没有指出其类型(使用的字节数)
- new分配的内存块通常和常规变量声明分配的内存块不同
- 常规变量在栈
- new从堆或者自由存储区分配内存
- 值为0的指针被称为空指针
- 使用delete,后面加上指向new分配的内存的指针
- 释放内存后,并不会删除指针本身
- 一定要成对使用new和delete,否则会发生内存泄漏
- 使用delete的关键,将它用于new分配的内存。不一定非是new的指针变量,如不同指针指向同一个new的内存的情况
- 一般来说,不要创建两个指向同一内存的指针,因为这将增加错误删除同一内存块两次的可能性。
- 对于大型数据(如数组,字符串和结构体),应当使用new
- 动态联编
- 静态联编
- 动态数组
- 使用new创建动态数组
int *p = new int [10]; // 返回第一个元素地址
delete [] p;
- new和delete遵循以下规则:
- 不能用delete释放不是new分配的内存
- 不要尝试释放已经释放的内存,结果未知
- 如果使用new[]为数组分配内存,则必须使用delete[]释放内存
- 如果使用new为单个对象分配内存,则必须使用delete(没有方括号)释放内存
- 对空指针使用delete是安全的
- 不能用sizeof运算符确定动态分配的数组包含的字节数
- 使用动态数组 4_18_arraynew.cpp
- 指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式
- C++将数组名解释为地址
- 指针变量加1,其增加的值等于指向的类型占用的字节数
- arr[1],C++编译器将该表达式看作是 *(arr + 1)
- 通常使用数组表示法,C++都执行下面转换
- arr[i] 转换成 *(arr + i)
- 使用的是指针,而不是数组名,C++也执行下面转换
- p[i] 转换成 *(p + i)
- 数组名和指针名区别:
- 可以修改指针的值,而数组名是常量
- 对数组应用sizeof运算符得到的是数组的长度,而对指针得到的是指针的长度,即使指针指向的是一个数组
- 数组的地址
int arr[10];
- 对数组取地址时,数组名并不会被解释为其地址
- 数组名解释为第一个元素的地址
- 对数组名取地址得到的是整个数组的地址
- 从数字上来说是一样,但从概念上讲&arr[0](即arr),是一个4字节内存块的地址,而&arr,是一个40字节内存块的地址
- 表达式arr+1,将地址值加2
- 表达式&arr+1,将地址加40
- arr是一个int指针(int *)
- &arr是一个,指向包含指向10个元素的int数组(int (*)[10])
int (*p)[10] = &arr;
- 声明指针
- 给指针赋值
- 对指针解除引用
- 区分指针和指针所指向的值
- 数组名
- 指针算术
- 数组的动态联编和静态联编
- 数组表示法和指针表示法
- 如果给cout提供一个字符的地址,则它会从该字符开始打印,直到遇到空字符为止
- 在C++中,用引号括起的字符串像数组名一样,也是第一个元素的地址 4_20_ptrstr.cpp
- 一般给cout提供一个指针,它将打印地址
- 但如果指针类型是char *,cout将显示指向的字符串
- 如果想要显示字符串的地址,则必须将这种指针强制转换成另一种指针类型,如int *
- 本节关于结构体的也适用于类
- new创建结构,需同时使用结构体类型和new
m_struct * ps = new m_struct;
- 结构体指针访问成员
- 使用 ->,如ps->m_mem;
- 使用(*ps).m_mem;
- C++11新增的线程存储,将在第9章简要讨论
- 自动存储:
- 在函数内部定义的常规变量使用自动存储空间
- 是局部变量
- 作用域为包含它的代码块
- 存储在栈中
- 静态存储:
- 静态存储是整个程序执行期间都存在的存储方式
- 静态变量
- 函数外定义的变量
- 声明使用static关键字
- 第9章详细介绍
- 动态存储
- new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。
- 自由存储空间(free store) 或堆(heap)
- new和delete可以在一个函数中分配,而在另一个函数中释放
- 数组、结构体和指针
- C++11提供auto
- 简要概述:
- 动态数组
- 运行阶段设置vector对象长度
- 可以在末尾附加新数据
- 可以中间插入新数据
- C++11中可用列表初始化,而C++98不行
- 基本实用知识:
- 头文件vector
- 包含在std名称空间
- 模板使用不同的语法指出它存储的数据类型
- vector类使用不用语法指定元素数
- 示例
vector<typeName> vt(n_elem);
- 声明创建一个名为vt的vector对象
- 存储n_elem个类型为typeName的元素:
- 其中参数n_elem可以是整型常量,也可以是整型变量。
- 简要概述:
- 位于std名称空间
- 对象长度固定
- 使用栈(静态内存分配)
- 需要包含头文件array
- C++11和C++98中可用列表初始化
- 示例
array<typeName, n_elem> arr;
- 声明创建一个名为arr的array对象
- 存储n_elem个类型为typeName的元素:
- 其中参数n_elem可以是整型常量,不可以是变量。
- 三者都可用标准数组法访问各个元素
- array对象和数组存储在栈
- vector对象和数组存储在堆
- array对象可直接赋值,而数组必须逐个复制
- vector和array对象可以以使用成员函数at()避免越界
a2.at(1) = 2.3
- cout.setf(ios_base::boolalpha); //C++新特点
- 通常cout显示bool值之前会先转换为int,但上述函数调用设置了一个标记,该标记命令cout显示true或false,而不是1或0
- 副作用:
- 顺序点:
- 复合语句(语句块):
- 逗号运算符
- 允许将两个表达式放到C++句法只允许放一个表达式的地方
- 声明中用来分割列表中的变量
- 逗号运算符是一个顺序点
- 逗号表达式的值是第二部分的值
- 逗号运算符优先级最低
- 关系表达式
- <
- <=
- ==
-
-
=
- !=