Skip to content

Latest commit

 

History

History
711 lines (582 loc) · 17 KB

03.第三章:数组、vector与字符串.md

File metadata and controls

711 lines (582 loc) · 17 KB

第三章:数组、 vector 与字符串

数组

#include <iostream>
#include <type_traits>
#include <typeinfo>

int main()
{
    auto b = {1, 2, 3};
    std::cout << typeid(b).name() << std::endl;
    //std::cout << std::is_same_v<decltype(b),int[3]> <<std::endl;
}


//程序输出:St16initializer_listIiE
//实际数据类型:std::initializer_list<int>   //类模板所实例化出的类型


int main()
{
    int b[] = {1, 2, 3};
    auto a = b;//编译可通过,但 a 的类型绝不是 int型数组, a 的类型实际上是 int*
    
    auto& a = b;//引用避免类型退化
    std::cout << std::is_same_v<decltype(a), int(&)[3]> << std::endl;
}

元素个数必须是一个常量表达式(编译期可计算的值)

#include <iostream>
#include <typeinfo>

int main()
{
    char str[] = "Hello";//简化写法
    std::cout << std::is_same_v<decltype(str),char[6]> << std::endl;
    //在字符串最后隐式+0 表示字符串结束
    char str[] = {'H','e','l','l','o''\0'};//char[6]
    
    char str[] = {'H','e','l','l','o'};//char[5]
    std::cout << std::is_same_v<decltype(str),char[5]> << std::endl;
}


int main()
{
    int * a[3];
    std::cout << std::is_same_v<decltype(a),int*[3]> << std::endl;
}

int main()
{
    int x1;
    int x2;
    int x3;
    // 指针数组
    int *a[3] = {&x1,&x2,&x3};
    // 数组的指针
   	int (*a)[3] = {&x1,&x2,&x3};
    //  (*)修饰的是对象a,  a的类型首先是一个指针,指针解引用之后是一个包含三个元素的数组
}

声明数组的引用

#include <iostream>
#include <typeinfo>

int main()
{
    int b[3];
    // 数组的引用
    int (&a)[3] = b;
    //int& a[3] = b;//错误,不支持这样的引用
    std::cout << std::is_same_v<decltype(a), int(&)[3]> << std::endl;
    
    
    int x1;
    int x2;
    int x3;
    //int& a[3]={x1, x2, x3};//错误,不能定义引用的数组
}

数组中的元素访问

#include <iostream>
#include <typeinfo>


int main()
{
    int a[3] = {1, 2, 3};
    std::cout << a[1] << std::endl;
    std::cout << std::is_same_v<decltype((a),int(&)[3]> << std::endl;
    // a 不能被放在等号左边,因为它不能被修改;如果放在右边, 类型会发生隐式转换
    auto b = a;//  -> int*;     
    // b 会转换成int*; 且指针会指向它所包含元素的第一个元素                                    
    
    const int x = 3;// x 作为表达式 (x) 使用时,它是左值;x是左值,但它不能在被放在等号左边
    std::cout << std::is_same_v<decltype((x),const int&> << std::endl;
    
}
                                        
int main() {

    int a[3] = {4, 2, 3};

    auto b = a;
    std::cout << std::is_same_v<decltype(b),int*> << std::endl;
    std::cout << *b << std::endl;
    std::cout << b[1] << std::endl;
    std::cout << b << std::endl;
    std::cout << &(a[0]) << std::endl;
} 
                                         
Program returned: 0
Program stdout
1
4
2
0x7ffe06b722dc
0x7ffe06b722dc                                       
#include <iostream>
#include <type_traits>

int main() {

    int a[3] = {4, 2, 3};

    auto b = a;
    std::cout << b[1] << std::endl;
	// b[1] -> *(b+1); 
    // b中保存了一块内存,把 b 中保存的内存 +(1 * int*)  ;相当于移动 4 个字节
    // 把指针移动4个字节来解引用以获取其中元素的值
    std::cout << *(a + 1) << std::endl;//输出为 a[1]
    std::cout << 1[a] << std::endl;//同上,也可以这么表示
    //std::cout << a[100] << std::endl;//访问溢出
    //std::cout << *(a + 100) << std::endl;//上述问题的本质
    //std::cout << a[-1] << std::endl;//能通过编译,但访问越界,非常危险
    
    int x = 100;
    int* ptr = &x;
    std::cout << *ptr << std::endl;
    std::cout << ptr[0] << std::endl;// 对指针也可以用[]
    std::cout << ptr << std::endl;
    
    //x[y]  ->  *(x + y)
}
Program returned: 0
Program stdout
2
2
100
100
0x7ffe923631d0

数组到指针的隐式转换

#include <iostream>
#include <type_traits>

int main() {
    int a[3] = {1, 2, 3};
    std::cout << a[0] << std::endl;//数组到指针进行了隐式转换再处理
    std::cout << a[4] << std::endl;//不会报错,因为编译不会对它进行检查
    
    sizeof(a);	//12,不会产生数组到指针转换
    decltype(a);  // int[3],不会产生数组到指针转换
    auto b = a; // decay, 退化     int*
    
    //可以通过声明引用来避免隐式转换
     auto& b = a; 	// int(&)[3]
}


int array[4] = {1, 2, 3 ,4};
//注意:不要使用 extern 指针来声明数组
extern int array[4];
extern int* array;//错误,运行期错误   //可编译,可链接    
//错误原因:数组和指针本质的区别,数组在内存空间保存值,指针保存的是首地址
//链接,省略了类型信息


// 1-> 00000001   ->01 00 00 00    实际,大端法
// 2-> 00000002   ->02 00 00 00
// 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
std::cout << array << std::endl;
Program stdout:
0x200000001


extern int array[];   //合法
std::cout << array << std::endl;//对应的地址
std::cout << array[0] << std::endl;//对应的值

获得指向数组开头与结尾的指针

#include <iostream>
#include <type_traits>


int main()
{
    int a[3] = {1, 2, 3};
    std::cout << &(a[0]) << std::endl;//指向数组开头元素的指针
    std::cout << a << std::endl;
    std::cout << &(a[3]) << std::endl;//指向数组结尾元素的指针
    std::cout << a + 3 << std::endl;
    
    std::cout << a << ' ' << &(a[0]) << ' ' << std::begin(a) << std::endl;
    std::cout << a + 3 << ' ' << &(a[2]) << ' ' << std::end(a) << std::endl;
    
    std::cout << a << ' ' << &(a[0]) << ' ' << std::cbegin(a) << std::endl;
    std::cout << a + 3 << ' ' << &(a[2]) << ' ' << std::cend(a) << std::endl;
    
    std::begin(a)   // int *
    std::cbegin(a)  // const int*
        
    auto b = a;
    //此时 b 不再是数组类型,因此不能再用begin和end获取数组开头和结尾元素
    std::cout << std::begin(b) << std::endl;
    std::cout << std::end(b) << std::endl;
    
    auto& b = a;//引用可以,b是a的一个别名
    std::cout << std::begin(b) << std::endl;
    std::cout << std::end(b) << std::endl;
}

extern int array[];
int main()
{
    //此处无法使用, 无法对 Unknown Bounded Array 使用
    std::cout << std::begin(array) << std::endl;
    std::cout << std::end(array) << std::endl;
}

指针算数:

#include <iostream>
#include <type_traits>

//	增加、减少
//	比较
//	求距离
//	解引用
//	指针索引

int main()
{
    int a[3] = {1, 2, 3};
    auto ptr = a;   // int*
    // 指针加减
    ptr = ptr + 1;
    auto ptr2 = a + 3;
    
    // 比较
    std::cout << (ptr == ptr2) << '\n';
    std::cout << (ptr != ptr2) << '\n';
    // 不建议对指针进行大于小于的比较
    // 如果指向一个数组的两个位置的话是可以
    // 不同数组不建议
    std::cout << (ptr > ptr2) << '\n';
    std::cout << (ptr < ptr2) << '\n';
    std::cout << (ptr >= ptr2) << '\n';
    std::cout << (ptr <= ptr2) << '\n';
    
    // 指针之间求距离, 与指针类型相关
    std::cout << ptr2 - ptr << '\n';
    
    // 指针索引
    std::cout << *ptr << '\n';
    std::cout << *a << '\n';// a 在之前被隐式转换成指针
    std::cout << ptr[0] << '\n';
    std::cout << *(ptr+1) << '\n';
}

数组的其他操作

#include <iostream>
#include <type_traits>

// 求元素个数
int main()
{
    int a[3];
    // 方法一
    std::cout << sizeof(a) << std::endl;
    std::cout << sizeof(int) <<std::endl;
    // 获取数组元素个数,使用 sizeof 不会退化
    std::cout << sizeof(a) / sizeof(int) << std::endl;
    
    // 方法二
    std::cout << std::size(a) << std::endl;
    
    // 方法三
    std::cout << std::end(a) - std::begin(a) << std::endl;
    std::cout << std::cend(a) - std::cbegin(a) << std::endl;
        
    auto b = a; // 对 b ,上述方法均失效,因为 b 是一个指针 int*
    //对 Unknown Bounded Array 也无法使用
}

元素遍历

#include <iostream>
#include <type_traits>
//基于元素个数
int main()
{
    int a[4] = {2, 3, 5, 7};
    
    size_t index = 0;
    while (index < std::size(a))
    {
        std::cout << a[index] << std::endl;
        index = index +1;
    }
}
//基于 (c)begin/(c)end
int main()
{
    int a[4] = {2, 3, 5, 7};
    
    auto ptr = std::cbegin(a); //int*
    while (ptr != std::cend(a))
    {
        std::cout << *ptr << std::endl;
        ptr = ptr +1;
    }
}
//基于 range-based for 循环
int main()
{
    int a[4]={1, 2, 3, 4};
    
    for (int x : a)
    {
        std::cout << x <<std::endl;
    }
}

C字符串

#include <iostream>
#include <cstring>

int main()
{
    char str[] = "Hello";	// char[6] null-terminated string
    char str_1[] = { 'H', 'e', 'l', 'l', 'o' };	// 不能用指针,因为没有'\0'不会停止
    auto ptr = str; // char*
    std::cout << strlen(str) <<std::endl;
    std::cout << strlen(ptr) <<std::endl;
}

多维数组

#include <iostream>
#include <type_traits>


int main()
{
    int x1[3];
     
    int x2[3][4];
    x2[0]   -> int[4];
    (int int int int) (int int int int) (int int int int)
    
    int x3[3][4][5];	// x3[3 ]  ->  int[4][5]
    
    std::cout << sizeof(int) << std::endl;
    std::cout << sizeof(x2[0]) << std::endl;
    std::cout << std::is_same_v<decltype(x2[0]), int(&)[4]> << std::endl;
}

// 聚合初始化                    
int main()
{
    int x2[3][4] = {1, 2, 3, 4, 5}; 
    int x3[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
    //(int, int, int, int), (int, int, int, int), (int, int, int, int)
    
    int x4[3][4] = {{1, 2, 3}, {4, 5, 6, 7}};
    int x5[3][4] = {{1, 2, 3, 4}, {5, 6, 7}};
    std::cout << x4[0][4] <<std::endl;
    std::cout << x5[1][4] <<std::endl;
    
    int x[][2] = {1, 2, 3, 4};
    int y[][3] = {1, 2, 3, 4};//   ->  y[2][3]
    std::cout << sizeof(y) <<std::endl;
}

// 多维数组索引与遍历
int main()
{
    int x2[3][4] = {1, 2, 3, 4, 5};
    std::cout << x2[0][3] << std::endl;//索引
    //for (auto p : x2) // 错误在于p隐式类型转换成了int*  无法在内嵌的for中使用begin和end
    for (auto& p : x2) 
    {       
       	for (auto q : p)
        {
            std::cout << q << '\n';//遍历
        }
    }    
}

int main()
{
    int x2[3][4][5] = {1, 2, 3, 4, 5};
    for (auto& p : x2) 
    {       
       	for (auto& q : p)
        {
            for (auto r : q)
            {
                std::cout << r << '\n';//遍历
            }
        }
    }    
}

int main()
{
    int x2[3][4] = {1, 2, 3, 4, 5};
    size_t index0 = 0;
    while (index0 < std::size(x2))
    {
        size_t index1 = 0;
        while (index1 < std::size(x2[index0]))
        {
            std::cout << x2[index0][index1] << std::endl;
            index1 = index1 + 1;
        }
        index0 = index0 + 1;
    }
}

指针与多维数组

#include <iostream>
#include <type_traits>

int main()
{
    int x2[3][4][5]; 
    auto ptr = x2;  //  ->  int (*ptr)[4][5] = x2;
    
    x2[1] ->  *(x2 + 1) // x2 移动一个元素所占的空间 
    // 不是移动一个int型(所以需要转换成指针), 这样才满足 + 1 后移动低维个数的int型
    // 转换为指针只会丢掉最高维的信息
}

// 使用类型别名来简化多维数组指针的声明
using A2 = int[4][5];

int main()
{
    int x2[3][4][5];
    // 也可以这样定义   A2 x2[3]; // 这种定义方式,维度排列从低到高
    A2* ptr = x2;
    auto ptr1 = ptr[0];
    
}

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(*ptr))
        {
            ptr2 = ptr2 + 1;
        }
        ptr = ptr + 1;
	}
}
// 作业:为什么range-based for不能使用
// 写一个for循环来遍历,再写一个基于指针的遍历。对比二者区别,为何for遍历不了
// C++ insights
#include <iostream>
#include <type_traits>

int main()
{
  int x2[3][4] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  int (*ptr)[4] = std::begin(x2);
  while(ptr != std::end(x2)) {
    int * ptr2 = std::begin(*ptr);
    while(ptr2 != std::end(*ptr)) {
      ptr2 = (ptr2 + 1);
    }
    
    ptr = (ptr + 1);
  }
  
  return 0;
}

vector

#include <iostream>
#include <vector>
#include <type_traits>

// vector 可在运行期动态改变元素个数
// 性能比内建数组差,更侧重易用性

int main()
{
    int a[3];
    int b[3] = a;	// 数组不支持复制,占太多资源
    std::vector<int> x;  // 创建 x 对象; 缺省,元素是0个;可插入、删除元素
    std::vector<float> y;
    std::vector<int> y;
    y = x;	// vector 支持复制

}

// 构造与初始化
int main()
{
    int a[3] = {1, 2, 3};
    std::vector<int> x = {1, 2, 3};
    std::vector<int> y(3); //y中包含3个元素,int型,且被初始化为0
    std::vector<int> z(3,1); //z中包含3个元素,int型,且被初始化为1
    std::vector<int> z = {1, 1, 1};
    std::vector<int> z1{3, 1};// z1包含2个元素,第一个是3,第二个是1
}

// 获取元素个数
int main()
{
    std::vector<int> x1;
    std::cout << x1.size() << std::endl;	
    std::cout << x1.empty() << std::endl;
    
    // 向其中添加一个元素 2 ; 在结尾插入
    x1.push_back(2);	// 运行期执行 
    std::cout << x1.size() << std::endl;	
    std::cout << x1.empty() << std::endl;
    
    x1.pop_back();	// 把数组最后一位弹出来,从x1中删除元素
    std::cout << x1.size() << std::endl;	
    
	// vector 比较
    // vector 的比较实际上是调用字典来比较
    // 从第一个元素开始比较哪个大
    std::vector<int> x1 = {1, 2, 3};
    std::vector<int> x2 = {1, 3, 2};
    std::cout << (x1 == x2) << std::endl;	
	std::cout << (x1 > x2) << std::endl;	
}

vector 中元素的索引与遍历:

#include <iostream>
#include <vector>

int main()
{
    int a[3] = {1, 2, 3};
    std::begin(a);      // 获取一个指针指向a中的第一个元素
    std::vector<int> x1 = {1, 2, 3};
    std::begin(x1);		// 获取一个东西指向a中的第一个元素,非指针
    auto b = std::begin(x1);
    auto e = std::end(x1);
    std::cout << x1[2] << std::endl;	// 索引
    std::cout << x1.at(2) << std::endl;	// 用[]可能会越界
}

// 元素遍历
int main()
{
    std::vector<int> x1 = {1, 2, 3};
    auto b = x1.begin();
    while ( b != x1.end() )
    {
        std::cout << *b << std::endl;
        b = b + 1;
    }
    for (auto val : x1)
    {
        std::cout << val << std::endl;
    }
    // 调用std::begin与std::end不再是一个指针
    // 返回的是 iterator 迭代器,模拟指针的行为
    auto b = std::begin(x1);
    auto e = x1.end;	//两种写法都可以
    
    // Iterator invalidation
    x1.push_back(3);   // 添加元素之前的 b 和 e 可能就失效了
}

// 多维vector
int main()
{
    int a[3][4];
    std::vector<std::vector<int>> x;
    x.push_back(std::vector<int>());
    x[0].push_back(1);
    std::cout << x[0][0] <<std::endl;
    
    // 聚合初始化,第一个vector包含了3个元素,第二个vector包含了2个元素
    std::vector<std::vector<int>> x1{{1, 2, 3}, {4, 5}};
    std::cout << x1[1][2] <<std::endl;
}

// 从 . 到 -> 操作符
int main()
{
    std::vector<int> x;
    std::cout << x.size() <<std::endl;
    
    // 使用 ptr 访问 x
    std::vector<int>* ptr = &x;
    std::cout << (*ptr).size() <<std::endl;
    std::cout << ptr -> size() <<std::endl;
    // 只要是指针指向某一个类的对象,就可以用 -> 操作符调用类所包含的方法
} 

string

#include <iostream>
#include <string>

int main()
{
    std::string x = "Hello World";
    std::string y = x;
    y = y + " !";
}

int main()
{	
    // 参照cppreference中string的构造函数
    std::string x(3,'a');	// 构造了 3 个字符,每个字符是 a
    // basic_string( const basic_string& other )
    std::string y(x);	// 更常用
    y = y + " !";
    std::cout << y << '\n';
}

// 其他方法
// 尺寸(size/empty)
// 比较、赋值、拼接、索引
// 转换为 C 字符串
int main()
{
    std::string x("hello world");
    std::string y("hello");
    std::cout << (y < x) << '\n';
    
    y = "New String";
    // 拼接
    y = y + x;
    //  "New String" + "hello world" 不合法
    y = y + "New String";// string 里定义的operator
    // y = "New String" + "hello world" + x ;// 不合法
    y = std::string("New String") + "hello world" + x ;// 构造临时对象
    y = std::string("New String") + "hello world" + " !" ;// 从左到右
    std::cout << y << '\n';
    // 索引
    std::cout << y[2] << std::endl;
    
    // 转换为 C 字符串
    auto ptr = y.c_str();// 看cppreference的定义
    // const CharT* c_str() const
    // Returns a pointer to a null-terminated character array 
    // with data equivalent to those stored in the string.
    std::cout << ptr << std::endl;
    std::cout << std::is_same_v<decltype(ptr),const char*> << std::endl;
}