Chapter 3 数组、vector与字符串
3-1 数组的引入
# include <iostream>
# include <type_traits>
int main()
{
int a;//int
int b[10]; //int[10]
std::cout << std::is_same_v<decltype(b),int[10]> << '\n';//输出为1
}
int x;
std::cin >> x;
int b[x]; //该表达式不合法,类型原本应为int[x],但在编译器内无法处理该x,x是在编译之后地运行期输入的
// variable length array C++
int和int[1]是不同的类型,尺寸上相同,但是操作是不同的
数组初始化
int b[3]; //缺省初始化
int b[3] = {1,2,3}; // 聚合初始化(aggregate initialization)
int b[3] = {1,2}; // 前两个元素用给出元素初始化,最后一个用0初始化
int b[3] = {}; // 用0初始化所有元素
int b[] = {1,2,3} // decltype(b) == int[3]
int b[]; // 不合法
# include <typeinfo>
int main()
{
auto b = {1,2,3};
std::cout << typeid(b).name() <<std::endl;//std::initializater_list<int>
}
数组不支持复制
int b[3] = {1,2,3};
auto a = b;// a的类型为指针
auto& a = b;// a的类型为int(&)[3]
b[3]为内建数组,考虑易用性,有vector、string
char str[] = "Hello"; //str为char[6]类型
char str[] = {'H','e','l','l','o'}; //str为char[5]类型
数组的复杂声明
指针的数组
int x1;
int x2;
int x3;
int* a[3] = {&x1,&x2,&x3}; // a中包含三个元素,每个元素都是指针
数组的指针
int b[3];
int (*a)[3] = &b; // a是一个指针,指向一个包含三个元素的数组
std::cout << std::is_same_v<decltype(a),int(*)[3]> << std::endl;//输出结果为1
声明数组的引用
int b[3];
int (&a)[3] = b;
C++当中不能定义引用的数组;
数组中的元素访问
int a[3] = {1,2,3};
// l-value:locator value--有地址的值,数组是左值
std::cout << a[0] << std::endl;//输出结果为1
//解释左值:
const int x = 3;
std::cout << std::is_same_v<decltype((x)),const int> << std::endl;//(x)表示x是一个表达式,此时输出为0
std::cout << std::is_same_v<decltype((x)),const int&> <<std::endl;//输出为1
int a[3] = {1,2,3};
auto b = a; //--->a作为右值,隐式转换为int*,指向a中的第一个元素a[0]所在的地址
x[y]—>会被翻译为*((x)+(y)),即x+y的解引用,需要防止数组溢出
3-2 从数组到指针
数组到指针的隐式转换
int a[3] = {1,2,3};
auto b = a; // decay
// sizeof和decltype等不会产生隐式转换
// 隐式转换会丢失部分类型信息
// 可以通过声明引用来避免隐式转换
auto& b = a; //--->不产生隐式转换,int(&)[3]
注意,不要用extern指针来声明数组
# include <iostream>
void fun();
extern int* array;
int main(){
std::cout << array[1] << std::endl;
}
//source.cpp
# include <iostream>
int array[4] = {1,2,3,4};
void fun()
{
std::cout << array << std::endl;
}
// 运行错误
// 不是编译错误:每一个cpp都没有问题
// 不是链接错误:main.cpp.o与source.cpp.o没有类型信息
// 为什么运行的时候会报错?
// array打印出来是一个十六进制的数字--->指针
// fun()函数打印的地址和main函数打印的地址不同
// main()会打印出array里面的数
// fun()会打印数组的首个元素地址
// 可行的方案
# include <iostream>
void fun();
extern int array[];
int main(){
std::cout << array[1] << std::endl;
}
//source.cpp
# include <iostream>
int array[4] = {1,2,3,4};
void fun()
{
std::cout << array << std::endl;
}
Unknown Bounded Array 声明: 在一个程序里面需要用,在主程序里面需要声明,但不关注该数组的size
extern int array[]; —> 不完整类型
获得指向数组开头和结尾的指针
# include <iostream>
void fun();
extern int array[];
int main(){
int a[3] = {1,2,3};
//(&a[0])/a指向数组开头的指针
std::cout << a + 3 << ' ' << &(a[3]) std::endl;
std::cout << std::begin(a) <<std::endl;
std::cout << std::end(a) << std::endl;
// std::cbegin(a); // const int*只能读,不能写
}
指针算数
int a[] = {1,2,3};
auto ptr = a;
auto ptr2 = a + 3;
// 加减
ptr = ptr + 1; // 指针指向下一个int*
// 比较
std::cout << (ptr == ptr2) << std::endl; // 指向相同指针的时候可以做比较,但是指向不同数组的时候不建议
// 求距离
int distance = ptr2 - ptr;
// 解引用
std::cout << *ptr << std::endl;
// 指针索引
std::cout << (ptr) << std::endl;
3-3 其他操作
求元素个数操作
int a[3];
// 方法1:sizeof() C当中就已经引入,但存在不便(如类型更新)
std::cout << sizeof(a) / sizeof(int) << std::endl;
// 方法2:std::size the best method
std::cout << std::size(a) << std::endl;
// 方法3:end - begin 不太好,会增加运行期的负担
std::cout << std::cend(a) - std::cbegin(a) << std::endl;
元素遍历
基于元素个数遍历
int main(){
int a[3] = {2,3,4};
size_t index = 0;
while (index < std::size(a))
{
std::cout << a[index] << std::endl;
index = index + 1;
}
}
基于?begin/?end遍历
int main(){
int a[3] = {2,3,4};
auto ptr = std::cbegin(a);
while (ptr != std::cend(a))
{
std::cout << *ptr << std::endl;
ptr = ptr + 1;
}
}
基于range-based for循环
int main(){
int a[3] = {2,3,4};
for (int x : a)
{
std::cout << x << std::endl;
}
}
C++ insights:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRt6JwdE-1652760807791)(C:\Users\quany\AppData\Roaming\Typora\typora-user-images\image-20220516200617226.png)]
3-4 C字符串
# include <cstring>
int main()
{
char str[] = "Hello"; // char[6] numm-terminated string
auto ptr = str; // char*
// 遍历到'\0'结束
// C语言还提供了:strlen、strcmp来支持C字符串相关操作
std::cout << strlen(str) << std::endl;
}
C字符串需要’\0’的支持
3-5 多维数组
多维数组的本质是数组的数组
int a[3][4]; // a[3]-->每个里面包含一个int[4]
int x2[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int x3[3][4] = {1,2,3,4,5};
多维数组的索引与遍历
int x2[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
x2[0][3]; // 索引
// 遍历方式1
for (auto& p : x2)
{
for (auto q:p)
{
std::cout << q << '\n';
}
}
// 遍历方式2
size_t index0 = 0;
while (index0 < 3)
{
size_t index1 = 0;
while (index1 < 4)
{
std::cout << x2[index0][index1] << std::endl;
index1 = index1 + 1;
}
index0 = index0 + 1;
}
// 遍历方式3,推荐进行x[0][0~3] x[1][0~3] x[2][0~3]遍历
size_t index1 = 0;
while (index1 < std::size(x2[0]))
{
size_t index0 = 0;
while (index0 < std::size(x2))
{
std::cout << x2[index0][index1] <<std::endl;
index0 = index0 + 1;
}
index1 = index1 + 1;
}
// 遍历方式4
int main()
{
int x2[3][4];
auto ptr = std::begin(x2);
while (ptr != std::end(x2))
{
auto ptr2 = std::begin(*ptr);
while (ptr2 != std::end(*ptr2))
{
std::cout << *ptr2 << std::endl;
ptr2 = ptr2 + 1;
}
ptr = ptr + 1;
}
}
【问题】为何不能用range-based for进行遍历?
3-6 vector
C++标准库中的一个类模板,模拟数组
序列容器,更侧重易用性
# include <iostream>
# include <vector>
int main()
{
int a[3];
int b[3] = a;//报错
std::vector<int> x; // vector<int>
std::vector<int> y;
y = x;//不会报错
}
vector可以复制,可以在运行期动态改变元素个数(付出性能成本)
std::vector<int> = {1,2,3}; // 聚合初始化
std::vector<int> = x(3);
std::vector<int> = x(3,1); // {1,1,1}
std::vector<int> = x{3,1}; // {3,1}
其他方法
// 计算元素个数
std::cout << x1.size() << std::endl;
// 判断是否为空
std::cout << x1.empty() << std::endl;
// 添加元素
x1.push_back(2);
// 删除元素
x1.pop_back(); // 弹出最后一个元素
// vector的比较
std::cout << (x1 == x2) << std::endl;
std::cout << (x1 < x2) << std::endl; // 按元素次序进行比较,从头开始,如果x1的第一个元素小于x2的第一个元素,就会返回为1,如果相等,比较下一个元素
vector的遍历
std::vector<int> = {1,2,3};
std::cout << x1[2] << std::endl; //返回3
std::cout << x1.at(2) << std::endl; //如果越界,会再运行期报错
auto b = std::begin(x1); // x1.begin()
auto a = std::end(x1); // x1.end()
while (b != x1.end())
{
std::cout << *b << std::endl;
b = b + 1;
}
// 或者
for (auto val : x1)
{
std::cout << val << std::endl;
}
std::begin(x1)不再是指针,而是迭代器
*b;--->解引用
b[1] --->*(b + 1) //下标访问
b = b + 1;---> 移动
std::cout << a - b << std::endl;// 两个相减求距离
vector相关的其他内容
// 迭代器失效
std::vector<int> = {1,2};
auto b = std::begin(x1); // x1.begin()
auto a = std::end(x1); // x1.end()
x1.push_back(3); // 可能b和a就不再有效了
多维vector
std::vector<std::vector<int>> x;
x.push_back(std::vector<int>());
x[0].push_back(1);
std::cout << x[0][0] << std::endl;
std::vector<int> x;
std::cout << x.size() << std::endl;
std::vector<int>* ptr = &x;
std::cout << (*ptr).size << std::endl;
std::cout << ptr->size() << std::endl; // 上下相同
3-7 string
是C++标准库中定义的一个类模板特化别名,用于内建字符串的替代品
# include <iostream>
# include <string>
int main()
{
std::string x = "Hello world";
std::string y = x; // std::string y(x);
y = y + '!';
std::string z(3,'a');
}
//支持size/empty
//支持比较
//支持赋值
//支持拼接
//支持索引
//可转换为C字符串
std::string y("Hello");
auto ptr = y.c_str(); // 转换为C字符串
|