第六章 C++ 常用库函数与宏定义
在 C++ 中,合理利用标准库提供的函数可以显著提高开发效率并减少出错概率。C++ 的流、容器和算法等都定义在 std 命名空间中,因此在编写程序时,通常需要在 #include 头文件之后引入以下声明:
using namespace std;
本章将详细梳理在信息学奥赛(如 CSP-J、GESP)中常用的库函数,涵盖数组操作、字符处理、数学计算、排序以及随机数生成等核心模块。
6.1 常用库函数详解
1. 数组的整体操作
头文件:<cstring>
在处理数组(特别是邻接矩阵或记忆化数组)时,通常需要对数组进行整体初始化、复制或比较。
* 数组初始化:memset(a, val, sizeof(a))
* 第二个参数 val 传入 0 或 -1 时,数组 a 中的每个元素都会被初始化为 0 或 -1。
* 若传入 0x7F,由于 memset 是按字节进行填充的,数组中每个 4 字节的整型元素将被填充为 0x7F7F7F7F(十进制约为 $2.13 \times 10^9$),在算法竞赛中常用来表示“无穷大”($\infty$)。
* 数据复制:memcpy(b, a, sizeof(a))
* 将数组 a 中的内容整体复制到数组 b 中,需确保 b 空间足够。
* 数组比较:memcmp(a, b, sizeof(a))
* 按字节比较数组 a 和 b 是否等价,若返回 0 则表示完全一致。
2. 字符操作
头文件:<cctype>
用于对单个字符进行类型判断和大小写转换:
* tolower(c) / toupper(c):将字符 c 转换为小写 / 大写。
* isdigit(c):判断 c 是否为十进制数字('0'~'9')。
* isalpha(c):判断 c 是否为英文字母(大写或小写)。
* isupper(c) / islower(c):判断 c 是否为大写字母 / 小写字母。
* isgraph(c):判断 c 是否为除空格外的可打印字符。
* isalnum(c):判断 c 是否为字母或数字。
3. 最大值与最小值
头文件:<algorithm>
max(a, b):返回a和b中的较大值。min(a, b):返回a和b中的较小值。
提示:在简单的数值比较中,也可以通过三目运算符自行实现,如
inline int max(int a, int b) { return a > b ? a : b; }。
4. 交换变量的值
头文件:<algorithm>
swap(a, b):交换变量a和b的值。
在手动实现时,可以使用引用传递:
inline void swap(int &a, int &b) {
int t = a;
a = b;
b = t;
}
5. 排序与反转
头文件:<algorithm>
sort(begin, end):将区间[begin, end)内的元素按升序排序。sort(begin, end, cmp):使用自定义的比较规则cmp进行排序。reverse(begin, end):反转区间[begin, end)内的元素顺序。
示例代码:
vector<int> v = {3, 1, 4, 1, 5};
sort(v.begin(), v.end()); // 升序:v = {1, 1, 3, 4, 5}
sort(v.begin(), v.end(), greater<int>()); // 降序:v = {5, 4, 3, 1, 1}
reverse(v.begin(), v.end()); // 反转:v = {1, 1, 3, 4, 5}
6. 立即退出程序
头文件:<cstdlib>
exit(0):强行终止程序运行并返回状态码0。此函数在深度优先搜索(DFS)中找到最优解需要立刻结束程序时非常实用。在使用前务必先输出计算结果。
7. 运行时间统计
头文件:<ctime>
在算法竞赛中,当程序可能面临运行超时(TLE)风险时,可以使用 clock() 函数获取当前时间,进行“卡时”操作:
double a = (double)clock() / (double)CLOCKS_PER_SEC;
通过在程序关键节点记录时刻并求差,可以精确计算出程序运行的时间间隔。
8. 断言机制
头文件:<cassert>
assert(condition):若条件condition为假(false),程序会立即崩溃并报错。可以通过在包含头文件前定义#define NDEBUG来关闭所有断言。
调试断言与错误处理的区别:
- 错误处理(如条件限制、输入重新引导)应对的是用户的错误。
- 断言(
assert)应对的是代码的 Bug。如果在计算中途产生了逻辑上绝不可能出现的数据(如人数算出来是负数),应使用断言促使程序崩溃,以便于调试期定位错误源码。
9. 现代随机数发生器
头文件:<random>,<chrono>
竞赛建议:弃用
<cstdlib>中的rand()函数。它的随机数分布不均匀,且最大随机值通常只有 32767,周期较短。
C++11 提供了性能更优异、周期长达 $2^{19937}-1$ 的梅森旋转算法生成器(mt19937):
方案 A(使用高精度系统时间作为种子):
#include <random>
#include <chrono>
// 使用当前高精度时间作为种子
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
// 生成 [0, 99] 的均匀整数
uniform_int_distribution<int> dist(0, 99);
int r = dist(rng);
// 生成 [0.0, 1.0) 的均匀浮点数
uniform_real_distribution<double> dist2(0.0, 1.0);
double d = dist2(rng);
方案 B(简化版,秒级种子):
mt19937 rng(time(0));
int r = rng() % 100; // 生成 0~99 之间的整数
提示:
time(0)返回秒级系统时间。如果在同一秒内多次运行该程序,生成的随机序列将会完全相同。在常规竞赛中一般足够使用,若需要更高强度的随机性,建议采用方案 A。
10. 数学函数
头文件:<cmath>
abs(x):求整数的绝对值(同时包含于<cstdlib>,注意与针对浮点数的fabs区分)。sin,cos,tan,asin,acos,atan:三角函数及反三角函数,角度单位为弧度。由于 $\tan(\frac{\pi}{4}) = 1$,可以用acos(-1.0)或atan(1) * 4获得高精度的常数 $\pi$。sinh,cosh,tanh:双曲函数。sqrt(x):计算非负实数x的算术平方根。ceil(x)/floor(x):分别向上取整(返回大于等于x的最小整数)和向下取整(返回小于等于x的最大整数)。注意参数与返回值均为浮点型。exp(x)/log(x)/log10(x):分别求 $e^x$、自然对数 $\ln x$、常用对数 $\lg x$。pow(a, b):计算 $a^b$。由于浮点数存在精度漂移,在需要精确整除的场景下,仍需手写快速幂。fmod(a, b):求浮点数a / b的余数。
11. C++17 最大公约数与最小公倍数
头文件:<numeric>
从 C++17 标准开始,标准库提供了内置的 gcd 与 lcm 函数,无需再手动编写欧几里得算法:
#include <numeric>
int g = gcd(12, 18); // g = 6
int l = lcm(12, 18); // l = 36
6.2 课后推荐练习题
通过以下题目,可以有效巩固常用库函数(如数学函数、字符处理函数、排序算法等)的使用:
- 题目编号:11 —— 两点间的距离
- 考点:本题需要计算笛卡尔坐标系下两点之间的欧几里得距离,适合练习
<cmath>中sqrt()以及乘方运算。
- 考点:本题需要计算笛卡尔坐标系下两点之间的欧几里得距离,适合练习
- 题目编号:42 —— 完整的苹果
- 考点:计算被虫子咬过的苹果数时,由于不完整的苹果也不能保留,需要结合
<cmath>中的ceil()向上取整函数进行精确计算。
- 考点:计算被虫子咬过的苹果数时,由于不完整的苹果也不能保留,需要结合
- 题目编号:92 —— 最大公约数
- 考点:适合练习手写最大公约数算法,或在 C++17 环境下直接调用
<numeric>头文件中的gcd函数。
- 考点:适合练习手写最大公约数算法,或在 C++17 环境下直接调用
- 题目编号:99 —— 区间排序
- 考点:练习使用
<algorithm>中的sort()函数对指定数组的特定子区间[l, r]进行升序重排。
- 考点:练习使用
- 题目编号:131 —— 最小公倍数
- 考点:通过最小公倍数与最大公约数的关系 $lcm(a, b) = \frac{a \times b}{gcd(a, b)}$ 来实现计算,可练习 C++17 的
lcm库函数。
- 考点:通过最小公倍数与最大公约数的关系 $lcm(a, b) = \frac{a \times b}{gcd(a, b)}$ 来实现计算,可练习 C++17 的
- 题目编号:136 —— 成绩排序
- 考点:本题为多关键字排序题,要求对结构体进行多级排序,可深入练习
sort()函数中自定义比较函数cmp的编写。
- 考点:本题为多关键字排序题,要求对结构体进行多级排序,可深入练习
- 题目编号:735 —— 统计数字字符的个数
- 考点:输入一段包含空格的字符串,遍历每个字符,可利用
<cctype>中的isdigit()函数快速统计数字字符的个数。
- 考点:输入一段包含空格的字符串,遍历每个字符,可利用
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com