ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

第2期|string、vector与数组

第2期|string、vector与数组 开篇如果用过C语言就会知道C语言里处理字符串要手动管内存、strcpy容易越界C的string帮你把这些脏活累活都干了。vector更是日常开发用得最多的容器没有之一。这一篇讲《C Primer》第3章的核心string的常用操作、vector的动态增长、数组与指针的关系、迭代器的正确使用。一、标准库stringstring表示可变长的字符序列使用string类型必须首先包含string头文件#include string初始化方式常用操作避坑1size()返回无符号数。如果for (int i 0; i s.size() - 1; i)当s为空时s.size() - 1是无符号下溢变成一个巨大的数循环失控。正确的做法是先判断empty()或者直接使用i s.size()或者使用i 1 s.size()如果确实需要遍历到倒数第二个元素或者用std::ssize得到有符号大小。避坑2字符串字面值和string相加时“”号两边至少有一个是string。hello world是错误的两个const char*不能相加但s1 world是对的。处理string对象中的字符下图为cctype头文件中的主要函数二、vector动态数组vector是标准库最常用的容器定义在vector中。它是一个能自动增长的动态数组。初始化常用操作v[n]下标访问不检查越界v.at(n)下标访问越界抛异常v.pop_back()删除尾部元素vector扩容机制面试高频vector在底层是连续内存。当size达到capacity时再push_back会触发扩容1. 分配一块更大的内存通常是原来的1.5倍或2倍因编译器而异2. 把旧元素拷贝/移动到新内存3. 销毁旧元素释放旧内存。关键后果扩容后所有指向旧内存的迭代器、指针、引用全部失效这就是为什么在循环中push_back要特别小心// 危险写法 for (auto it v.begin(); it ! v.end(); it) { v.push_back(*it); // 可能触发扩容it失效 }性能优化如果知道大概需要多少元素用v.reserve(n)预先分配容量避免多次扩容拷贝。三、迭代器迭代器是STL的核心概念是容器和算法之间的桥梁。所有标准库容器都支持迭代器。auto it v.begin(); // 指向第一个元素 auto end v.end(); // 指向最后一个元素的下一个位置尾后迭代器 *it; // 解引用获取元素 it; // 移动到下一个元素标准容器迭代器运算符迭代器类型1. begin/end普通迭代器可读写2. cbegin/cendconst迭代器只读3. rbegin/rend反向迭代器从尾向头遍历。迭代器失效场景总结操作哪些迭代器失效vector插入导致扩容全部失效vector中间插入插入点之后全部失效vector删除元素删除点之后全部失效list插入/删除仅被删元素的迭代器失效vector和string迭代器支持的运算四、数组与指针C保留了C风格数组但《C Primer》明确建议优先使用vector和string数组只在性能极端敏感或与C接口交互时使用。数组的特点1. 大小固定编译时确定2. 数组名在大多数表达式中会退化为指向首元素的指针3. 数组不能拷贝或赋值4. 数组越界是未定义行为编译器不检查5. 默认情况下数组的元素被默认初始化6. 不存在引用的数组7. 当使用字符串字面值对数组进行初始化时一定要注意字符串字面值的结尾处还有一个空字符\0。unsigned cnt 42; // 不是常量表达式 constexpr unsigned sz 42; // 常量表达式 int arr[10]; // 含有10个整数的数组 int *parr[sz]; // 含有42个整型指针的数组 int refs[10] /*?*/; // 错误不存在引用的数组 int (*parray)[10] arr; // parray指向一个含有10个整数的数组 int (arrRef)[10] arr; // arrRef引用一个含有10个整数的数组 string bad[cnt]; // 错误cnt不是常量表达式 string strs[get_size()]; // 当get_size是constexpr时正确否则错误 int arr[5] {1, 2, 3, 4, 5}; int *p arr; // arr退化为arr[0] *(p 2); // 等价于arr[2]值为3指针和数组的关系下标操作本质上是指针运算。arr[i]等价于*(arr i)。但指针是独立变量可以改指向数组名是常量地址不能改。多维数组C的多维数组本质是“数组的数组”。int a[3][4]是一个包含3个元素的数组每个元素是一个包含4个int的数组。标准库函数begin和end定义在iterator头文件中:为了让指针的使用更简单安全C11引入了begin和end。int ia[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int *beg begin(ia); // 指向ia的首元素 int *last end(ia); // 指向ia尾元素的下一位置的指针多维数组使用范围for语句处理多维数组size_t cnt 0; for (auto row : ia) { for (auto col : row) { col cnt; cnt; } }注意要使用范围for语句处理多维数组除了最内层的循环外其他所有循环的控制变量都应该是引用类型为了避免数组被自动转换成指针导致编译错误。指针和多维数组因为多维数组实际上是数组的数组所以由多维数组名转换得来的指针实际上是指向第一个内层数组的指针。int ia[3][4]; // 大小为3的数组每个元素是含有4个整数的数组 int (*p)[4] ia; // p指向含有4个整数的数组 p ia[2]; // p指向ia的尾元素C11通过auto或者decltype就能尽可能地避免在数组前面加上一个指针类型了for (auto p ia; p ! ia 3; p) for (auto q *p; q ! *p 4; q) cout *q ; cout endl;或者for (auto p begin(ia); p ! end(ia); p) for (auto q begin(*p); q ! end(*p); q) cout *q ; cout endl;本期小结1. string自动管理内存注意size()无符号下溢和字符串拼接规则2. vector扩容会使所有迭代器失效预知大小时用reserve优化3. 迭代器是容器和算法的桥梁不同容器的失效规则不同4. 数组名退化为指针数组大小固定优化使用vector替代。下一期预告第3期——类基础构造、析构、访问控制、const成员函数、友元等。
返回列表