#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;
}
}
#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;
}
#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;
}
#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;
// 只要是指针指向某一个类的对象,就可以用 -> 操作符调用类所包含的方法
}
#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;
}