news 2026/9/19 5:08:38

C++ 常用标准库函数实战避坑:string、vector、algorithm

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 常用标准库函数实战避坑:string、vector、algorithm

简介:这是一份面向C++初学者与需要随时查阅标准库接口的开发者整理的常用函数速查文档,针对日常编程中数学运算、字符串处理、内存操作与类型转换等高频需求,把零散的函数原型与返回值说明汇集到一处,便于快速定位与对照使用。压缩包内仅1个pdf文件,约93KB,篇幅精炼、便于打印或放在本地随时翻阅。内容按数学函数、字符串处理函数、其他常用函数及输入输出成员函数分类罗列,涵盖abs、fabs、pow、sqrt、log等数学计算,memcpy、memset、strcpy、strcat、strcmp、strstr等字符串与内存操作,以及abort、exit、atof、atoi、rand、srand等实用接口,并逐条标注函数原型、功能描述与返回值含义,同时说明所属头文件与使用时的语法语义注意事项,能帮助读者减少翻查手册的时间、降低误用风险。目前已有984人学习下载,适合作为课堂学习、课后练习与项目开发的案头参考。

1. C++ 常用标准库函数,背表不如记住「哪个头文件加什么参数」

打开任何一份 C++ 八股整理,标准库那一页永远是密密麻麻的函数表:string 的 substr、find、append,vector 的 push_back、resize、reserve,algorithm 的 sort、lower_bound、unique。背下来不难,难的是换成一道真实任务——读一行逗号分隔的数字塞进数组、按结构体某个字段倒序排、在有序数组里找第一个不小于 x 的位置——手就停了,或者写出来编译能过、跑起来结果不对。

围绕「C++ 常用标准库函数」的梳理要解决的就是这个落差。按<string><vector><map><algorithm><sstream>这几个日常出场率最高的头文件,把真正高频的那批函数挑出来,说清它在哪个头文件、参数怎么传、返回值什么时候是坑、失败时该看哪一行。

从刚配好 VSCode C/C++ 环境、还在纠结c_cpp_properties.json的新手,到工作几年仍然频繁查 cppreference 的老手,都能在这里找到一两条自己没注意过的边界:find返回的npos不能和-1比,reserve不改 size,sort的比较器写<=会在特定数据上直接崩。

2. string 与 vector 的常用函数:从初始化到容量控制

这两个头文件承担了日常代码里八成以上的调用量,也集中了一半以上的运行时错误。它们的共同点是:函数名都很好记,参数含义却容易记反。

2.1 string 上最高频的四个函数:find、substr、size、append

find返回的是std::size_t,找不到时返回std::string::npos,它是size_t的最大值,不是-1。把返回值存进int再和-1比较,是新手最常见的翻车点。

#include <string> #include <iostream> int main() { std::string s = "C++ standard library"; std::size_t p = s.find("standard"); // 找不到返回 std::string::npos if (p != std::string::npos) { std::string sub = s.substr(p, 8); // 从下标 p 开始取 8 个字符 std::cout << sub << '\n'; // 输出 standard } std::cout << s.size() << '\n'; // 20,等价于 s.length(),O(1) }

substr(pos, n)的第二个参数是「最多取多少个」,省略则取到末尾;如果pos > size(),它抛std::out_of_range,而不是返回空串。find的第二个参数可以指定起始下标,第三个参数用于查找字符数组,带n的那个重载能避免对 C 字符串做隐式strlen扫描。

2.2 vector 的 size、capacity、reserve、resize 差在哪

这四个函数经常被当作同义词用,实际语义完全不同:size是已有元素个数,capacity是已分配内存能装多少个,reserve只动 capacity,resize只动 size。

#include <vector> #include <iostream> int main() { std::vector<int> v; v.reserve(1000); // capacity 至少 1000,size 仍是 0 // v[0] = 1; // 错误:size 为 0,越界写 for (int i = 0; i < 10; ++i) v.emplace_back(i * i); v.resize(5); // size 变 5,多余元素被析构;capacity 不变 std::cout << v.size() << ' ' << v.capacity() << '\n'; // 5 1000 }

判断该用push_back还是emplace_back:元素类型有非平凡的构造函数时,emplace_back直接在容器内存里构造,省一次移动;对intdouble这类平凡类型两者没差别。用reserve预分配再emplace_back,能消掉扩容时的整块搬迁,这是刷题时最划算的一行优化。

2.3 字符串数组初始化与二维 char 数组的边界

std::string arr[3] = {"a", "bb", "ccc"}char arr[3][8] = {"a", "bb", "ccc"}看着像,行为差很远:前者每个元素是独立对象,长度可变;后者每行固定 8 字节,超长会截断或编译报错,且必须留出结尾的'\0'

#include <cstring> #include <string> char fixed[3][8] = {"a", "bb", "ccc"}; // 每行最多 7 个可见字符 + '\0' std::string dyn[3] = {"a", "bb", "ccc"}; // 长度自由 // sizeof(fixed) == 24,sizeof(fixed[0]) == 8 // strlen(fixed[1]) == 2,不要写成 sizeof(fixed[1]) / sizeof(char)

混用sizeofstrlen是 C 风格数组的老问题:sizeof得到的是数组总字节数(包含所有'\0'和填充),strlen只数到第一个'\0'。要拿行数就用sizeof(fixed) / sizeof(fixed[0])

2.4 map 与 unordered_map:operator[] 和 find 的取舍

operator[]在键不存在时会默认构造一个值并插入,所以「只读查询」绝不能用它,否则容器会被悄悄改大。

#include <map> #include <string> std::map<std::string, int> cnt; cnt["apple"]++; // 不存在则插入 0 再自增 auto it = cnt.find("pear"); if (it != cnt.end()) { /* 只读查询用 find,不改变容器 */ } std::size_t n = cnt.count("pear"); // map 下只能是 0 或 1
函数头文件生效对象常见误用
size / length<string>字符个数,O(1)与 int 混算出现负数
find<string>首次出现下标拿 npos 和 -1 比较
substr<string>子串pos 越界抛异常
reserve<vector>仅 capacity以为能直接下标赋值
resize<vector>仅 size缩小后仍访问旧下标
operator[]<map>读或插入只读查询污染容器

3. algorithm 下的高频函数:sort 比较器与二分查找

<algorithm>是标准库里性价比最高的一个头文件:几十行手写代码,换成一个函数调用,而且复杂度有保证。前提是你得知道每个函数对数据状态的要求。

3.1 sort 的引入库与比较器必须满足严格弱序

sort定义在<algorithm>里,只写#include <vector>编译会报「未声明的标识符」。它的第三个参数是比较器,必须满足严格弱序:comp(a, a)必须为假。

#include <algorithm> // sort 属于 <algorithm>,不是 <vector> #include <vector> #include <string> struct Student { std::string name; int score; }; int main() { std::vector<Student> v{{"a", 90}, {"b", 85}, {"c", 90}}; std::sort(v.begin(), v.end(), [](const Student& x, const Student& y) { if (x.score != y.score) return x.score > y.score; // 分数降序 return x.name < y.name; // 同分按名字升序 }); }

写成return x.score >= y.score;就破坏了严格弱序,元素相等时比较器返回真,std::sort的实现在遇到重复键时可能越界访问,表现为随机崩溃或死循环,而不是编译错误。比较器还要注意捕获方式:[&]捕获的变量如果在排序期间被修改,同样会破坏有序性假设。

3.2 lower_bound 与 upper_bound:二分查找不用手写

只要区间已按同一个比较器排好序,lower_bound返回第一个不小于目标的位置,upper_bound返回第一个大于目标的位置,两者相减就是等于目标值的元素个数。这是「C++ 二分查找」类题目最稳的写法。

#include <algorithm> #include <vector> #include <iostream> int main() { std::vector<int> a{1, 3, 3, 5, 9}; auto lo = std::lower_bound(a.begin(), a.end(), 3); // 第一个 >= 3 auto hi = std::upper_bound(a.begin(), a.end(), 3); // 第一个 > 3 std::cout << (lo - a.begin()) << ' ' // 1 << (hi - a.begin()) << ' ' // 3 << (hi - lo) << '\n'; // 2 }
函数前提复杂度典型用途
sort随机访问迭代器O(n log n)通用排序
stable_sort随机访问迭代器O(n log²n)需要保留相等元素原序
lower_bound区间已按同一比较器有序O(log n)找第一个 >= x
upper_bound同上O(log n)找第一个 > x
unique相邻重复才合并O(n)必须先排序
remove_if无序要求O(n)配合 erase 删除

降序数组上直接调lower_bound,得到的是无意义的结果——它不会报错,只是默默给出错误答案,查错时优先确认这一点。降序容器要显式传std::greater<int>()

3.3 unique + erase 去重链,以及 count_if、find_if

unique只合并相邻的重复元素,所以排序是必要前置。它返回新的逻辑尾,容器长度并不改变,必须再配合erase才真正删除。

#include <algorithm> #include <vector> std::vector<int> v{3, 1, 3, 2, 1}; std::sort(v.begin(), v.end()); // 1 1 2 3 3 v.erase(std::unique(v.begin(), v.end()), v.end()); // 1 2 3 std::size_t odd = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 1; }); // 2 auto it = std::find_if(v.begin(), v.end(), [](int x) { return x > 1; }); // 指向 2

3.4 判断质数这类小算法的标准库写法

「判断质数 C++ 优化」的常见写法是用i * i <= n代替sqrt,避免浮点误差,也省一次类型转换。

bool is_prime(long long n) { if (n < 2) return false; for (long long i = 2; i * i <= n; ++i) // 等价于 i <= sqrt(n),无浮点误差 if (n % i == 0) return false; return true; }

如果循环上界需要动态计算,std::sqrt返回值要向下取整后加一处理,别直接拿浮点结果当边界。累加类操作则交给<numeric>里的std::accumulate,第三个参数的类型决定累加结果的类型,写0会在数据量大时溢出,长整型场景要写0LL

4. 流 I/O 与数值转换:字符串转数组、stoi 的异常边界

命令行工具和算法题里最常见的输入形态是「一行逗号分隔的数字」,而cin >>遇到逗号会停下。这时候要换sstream,不是继续给cin加格式。

4.1 cin 与 getline 混用的经典坑

cin >> n会跳过前导空白但在缓冲区留下换行符,紧跟的getline读到的就是一个空行。

#include <iostream> #include <string> int main() { int n; std::cin >> n; std::string line; std::getline(std::cin, line); // 先吃掉残留的换行,否则下一行为空 std::getline(std::cin, line); // 这次才是真正的第一行内容 std::cout << line << '\n'; }

排查这类问题时,先看std::cin.fail()std::cin.eof()的状态位:解析失败后cin会进入失败状态,必须std::cin.clear()清除标志,再丢弃缓冲区里的残留字符,否则后续所有读入全部无效。

4.2 用 istringstream 把字符串转成数组

#include <sstream> #include <string> #include <vector> std::vector<int> parse_ints(const std::string& s) { std::vector<int> out; std::istringstream iss(s); std::string token; while (std::getline(iss, token, ',')) { // 以逗号为分隔符逐段取 if (!token.empty()) out.push_back(std::stoi(token)); } return out; }

std::getline(iss, token, ',')的第三个参数是分隔符,不传则按换行切分。用while (iss >> token)则按空白切分,遇到逗号不会断开。两种方式按输入格式二选一,别混用。

4.3 stoi、stod 的参数顺序与异常类型

std::stoi的签名是stoi(const string&, size_t* pos = 0, int base = 10),第二个参数回传「解析停止的位置」,传nullptr表示不关心。

#include <string> #include <iostream> int main() { std::size_t pos = 0; std::string s = "42 3.14"; int a = std::stoi(s, &pos); // a = 42,pos = 2 double b = std::stod(s.substr(pos)); // 3.14 std::cout << a << ' ' << b << '\n'; try { int bad = std::stoi("abc"); // 抛 std::invalid_argument } catch (const std::invalid_argument&) { std::cout << "no digits\n"; } catch (const std::out_of_range&) { std::cout << "too large\n"; } }

stoi("12abc")不抛异常,返回 12;stoi("999999999999")out_of_range。这两个行为差异是「字符串转数组」时最常见的静默 bug 来源。

4.4 to_string 与格式化输出

std::to_string(double)固定输出 6 位小数,需要控制精度就得回到流。

#include <iomanip> #include <sstream> #include <string> std::string fmt(double x) { std::ostringstream oss; oss << std::fixed << std::setprecision(3) << x; // 保留 3 位小数 return oss.str(); } std::string a = std::to_string(3.14159); // "3.141590" std::string b = fmt(3.14159); // "3.142"
函数失败行为是否抛异常
stoi / stol无数字抛 invalid_argument
stoi超出 int 范围抛 out_of_range
stoi("12abc")解析到非法字符即停
stod("nan")返回 NaN
to_string无失败路径

setw只对下一次输出生效,需要每行都对齐就得在循环里重复调用,这一点和setprecision的粘性行为不同。

5. 把标准库函数用出边界:lambda 回调、编译环境与护栏

5.1 用 lambda 和 std::function 传回调

sortfor_eachtransform都接受可调用对象。需要把回调存起来延迟调用时用std::function,只在当次调用中使用则直接用 lambda,代价更低。

#include <functional> #include <vector> void for_each_even(const std::vector<int>& v, const std::function<void(int)>& cb) { for (int x : v) if (x % 2 == 0) cb(x); } int main() { std::vector<int> v{1, 2, 3, 4}; int sum = 0; for_each_even(v, [&sum](int x) { sum += x; }); // 引用捕获,注意生命周期 // sum == 6 }

捕获列表写成[&]时,回调一旦被存进std::function并延迟到原作用域之外执行,引用就悬空了。跨作用域的回调要么按值捕获,要么明确保证被引用对象的生存期长于回调。

5.2 VSCode 配置与 MSVC 运行库报错的排查顺序

「microsoft visual c++ 14.0 is required」这类报错要分成两种,处理方式完全不同:

报错时机常见原因处理方向
安装第三方包时报 14.0 required缺少 MSVC 编译工具链安装对应版本的生成工具
编译期 fatal error C1083找不到头文件检查编译器路径与 includePath
运行 exe 时缺 DLL缺少 Microsoft Visual C++ Redistributable安装对应架构的运行库

VSCode 的智能提示和实际编译是两条链路,c_cpp_properties.json只影响补全,改完仍报找不到头文件,要看tasks.json里编译器实际用的是哪个。

{ "configurations": [ { "name": "Win32", "includePath": ["${workspaceFolder}/**"], "compilerPath": "C:/msys64/ucrt64/bin/g++.exe", "cppStandard": "c++17", "intelliSenseMode": "windows-gcc-x64" } ], "version": 4 }

includePath的优先级是「先匹配先生效」,把标准库路径写在自己项目路径前面,会导致同名头文件被错误解析。

5.3 用编译告警和 static_assert 给标准库调用加护栏

标准库的很多坑属于隐式转换:size_tint相减、double窄化成intconst char*std::string混比。把告警打开,编译器能替你抓出大部分。

g++ -std=c++17 -Wall -Wextra -Wconversion -Wshadow main.cpp -o main
#include <cstddef> #include <vector> static_assert(sizeof(std::size_t) == 8, "该模块只在 64 位平台构建"); std::size_t idx = 0; int n = 10; // if (idx < n) ... // -Wsign-compare 会在这里报警

-Wconversion专门盯窄化转换,-Wsign-compare盯有符号无符号比较,两者恰好覆盖了size()参与算术运算时最常出的错误。再写一个小的自检程序,把find返回nposlower_bound在降序数组上的表现各跑一遍,比读十遍文档记得牢。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 5:02:28

数字人技术:能力边界与最佳应用场景解析

1. 数字人与真人的边界探讨最近几年&#xff0c;数字人技术突然火了起来。从虚拟主播到AI客服&#xff0c;从数字员工到元宇宙居民&#xff0c;各种形态的数字人层出不穷。作为一个长期关注人机交互领域的技术从业者&#xff0c;我经常被问到&#xff1a;"数字人最终能完全…

作者头像 李华
网站建设 2026/9/19 5:19:58

信创产品评估认证全流程解析与核心技术维度

1. 信创产品评估证书的行业背景解析信创产业作为国家信息技术应用创新战略的重要载体&#xff0c;近年来在基础硬件、基础软件、云计算、大数据等领域快速发展。在这个背景下&#xff0c;信创产品评估证书应运而生&#xff0c;成为衡量国产化软件技术水平和安全可靠性的重要标尺…

作者头像 李华
网站建设 2026/9/19 5:20:44

数据类型本质:比特、字节与解释协议的底层真相

1. 这不是抽象概念&#xff0c;是写代码时每秒都在打交道的底层现实“8种数据类型和位、字节、比特的关系”——看到这个标题&#xff0c;别急着划走。它不是教科书里冷冰冰的定义堆砌&#xff0c;而是你每天敲int a 5;、调试pandas.read_csv()报错、排查 Wireshark 里只显示5…

作者头像 李华
网站建设 2026/9/19 5:09:25

STM32CubeProgrammer安装与ST-Link连接全指南

1. 为什么STM32CubeProgrammer不是“装个软件就完事”的工具&#xff1f;你可能刚在B站刷到一个标题叫《AI一键生成STM32代码》的视频&#xff0c;点进去发现最后一步是“打开STM32CubeProgrammer烧录”&#xff0c;然后弹出个黑窗口闪一下——程序跑起来了。但如果你真去试&am…

作者头像 李华
网站建设 2026/9/19 5:19:09

Agent OS 安全工具开发实战:基于 ATR 构建可复用的 Custom Tools

Agent OS 安全工具开发实战&#xff1a;基于 ATR 构建可复用的 Custom Tools 【免费下载链接】agent-governance-toolkit AI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI age…

作者头像 李华