资讯动态

C/C++中比较字符串的方法详解

发布时间:2026/10/26 3:11:59 来源:尧图企业网站定制
前言「比较字符串」在 C 和 C 里是两套不同的东西。C 的字符串是以\0结尾的字符数组比较用strcmp、strncmp传的是指针C 的std::string是带长度和容量管理的类比较可以直接用、也可以用成员compare。把两者混用最常见的结果是「比较的是指针地址而不是内容」或者「越界读」。另一个常见误解是认为strcmp返回 -1、0、1。标准只保证返回值的符号小于、等于、大于分别对应负值、0、正值具体数值由实现决定。把返回值存起来与 -1 比较在多数实现上碰巧能工作但不是可移植的写法。本文分「C 风格」「C 风格」「忽略大小写」「排序与本地化」四部分说明每种方法的语义、返回值和边界并给出一个不依赖平台的大小写无关比较实现。一、C 风格字符串比较1.1 strcmpstd::strcmp(lhs, rhs)声明在cstringC 中为string.h逐字符比较直到遇到\0或出现差异。返回值关系返回值lhs小于rhs负值相等0lhs大于rhs正值「小于」按字符的unsigned char值比较因此结果不受普通char是否有符号影响。这一点很重要如果实现按有符号char比较非 ASCII 字节如 UTF-8 中的中文可能得到相反的次序。两个参数都必须是指向以\0结尾的字符数组的有效指针。传nullptr是 UB传没有结尾的数组会越界读。比较的是内容不是地址所以strcmp(a, b) 0与a b完全不是一回事。1.2 strncmpstd::strncmp(lhs, rhs, n)最多比较n个字符遇到\0提前结束。它常用于判断前缀/* 适用于 C11 */#include string.hint hasPrefix(const char* text, const char* prefix) {return strncmp(text, prefix, strlen(prefix)) 0;}要注意n是「最多比较的字符数」不是「两个字符串的长度都必须至少为 n」。如果prefix比n短比较会在\0处结束如果text更短也会在\0处结束。前缀判断还需要额外确认text不短于prefix否则text ab、prefix abc时strncmp会在text的\0与c比较时返回负值结果恰好正确但若prefix更长且内容恰好是text的前缀仍会返回非零不会误判。真正需要小心的是n超过任一缓冲区长度的情况。1.3 memcmp 不是字符串比较std::memcmp(lhs, rhs, n)按字节比较固定长度不关心\0。它适合比较二进制数据或已知长度的缓冲区。用它比较字符串时必须保证两块内存都至少有n字节否则越界而且它不会在\0处停止可能把字符串结尾之后的垃圾字节纳入比较。二、C 的 std::stringstd::string重载了比较运算符、!、、、、都按字典序比较内容复杂度 O(n)。s1 abc可以直接写因为std::string与const char*之间有比较重载但abc s1也可以顺序不影响。成员compare提供更细的控制// 适用于 C17#include iostream#include stringint main() {const std::string a apple;const std::string b banana;if (a b) {std::cout apple before banana\n;}const int result a.compare(b);if (result 0) {std::cout a b\n;} else if (result 0) {std::cout a b\n;} else {std::cout a b\n;}return 0;}compare的返回值同样只保证符号不要写 -1。它还有带位置和长度的重载例如s.compare(pos, len, other)比较s从pos开始的len个字符与otherpos越界会抛std::out_of_range。这类重载适合解析固定格式的字段但下标计算容易出错能用substr或string_view表达清楚时优先用后者。std::string可以包含\0因为长度单独存储而strcmp遇到\0就停。两者对「含空字符的字符串」的行为不同跨边界传递时要明确用哪一种。三、忽略大小写比较标准库没有直接提供「忽略大小写的字符串相等」函数。可以自己写关键是把字符转成unsigned char再传给std::tolower// 适用于 C17#include cctype#include cstddef#include string_viewbool equalsIgnoreCase(std::string_view lhs, std::string_view rhs) {if (lhs.size() ! rhs.size()) {return false;}for (std::size_t i 0; i lhs.size(); i) {const unsigned char a static_castunsigned char(lhs[i]);const unsigned char b static_castunsigned char(rhs[i]);if (std::tolower(a) ! std::tolower(b)) {return false;}}return true;}std::tolower的参数是int且只对「可表示为unsigned char的值或 EOF」有定义。直接传负的char是 UB所以先转unsigned char再提升为int。这个实现只处理单字节字符对 UTF-8 中文大小写转换没有意义且按字节比较仍然可用相同字节序列相等但不要把tolower用在多字节序列上。如果需要按本地化规则比较例如某些语言的重音、连字应使用std::collate或 C 的strcoll并设置合适的 locale。这类比较的结果依赖平台和 locale 数据跨平台行为可能不同。四、排序与选择场景推荐方法说明C 字符串相等strcmp(a, b) 0参数必须非空且以\0结尾C 字符串前缀strncmp 长度检查注意n的含义二进制块memcmp需要已知长度std::string相等内容比较可含\0std::string排序或std::less字典序部分比较compare(pos, len, ...)注意越界异常忽略大小写自定义逐字符比较tolower前转unsigned char本地化排序std::collate/strcoll依赖 locale在std::mapstd::string, T中键的排序默认使用std::lessstd::string即字典序如果业务需要忽略大小写不能只重载还要提供对应的比较器并保证它满足严格弱序。常见坑点❌ 用比较两个char*。/* 适用于 C11 */const char* a abc;const char* b abc;if (a b) { /* 比较的是地址不是内容 */ }✅ 用strcmp。/* 适用于 C11 */if (strcmp(a, b) 0) { /* ... */ }❌ 把strcmp的返回值与 -1 比较。/* 适用于 C11 */if (strcmp(a, b) -1) { /* 标准只保证负值 */ }✅ 只判断符号。/* 适用于 C11 */if (strcmp(a, b) 0) { /* ... */ }❌ 向strcmp传空指针。/* 适用于 C11 */strcmp(nullptr, abc); // UB✅ 先检查指针。/* 适用于 C11 */if (a ! NULL b ! NULL strcmp(a, b) 0) { /* ... */ }❌ 用strncmp比较两个std::string。// 适用于 C17strncmp(s1.c_str(), s2.c_str(), s1.size()); // 长度不同或含 \0 时不可靠✅ 直接用std::string的比较运算符。// 适用于 C17if (s1 s2) { /* ... */ }❌ 把std::string::compare的返回值当布尔值。// 适用于 C17if (s.compare(abc)) { /* 相等时返回 0条件为假非零时反而为真 */ }✅ 显式比较是否为 0。// 适用于 C17if (s.compare(abc) 0) { /* ... */ }❌ 把负的char直接传给std::tolower。// 适用于 C17char c static_castchar(0xE4);std::tolower(c); // 参数为负UB✅ 先转unsigned char。// 适用于 C17std::tolower(static_castunsigned char(c));❌ 用memcmp比较字符串而不检查长度。/* 适用于 C11 */memcmp(a, b, 100); // 任一缓冲区不足 100 字节就越界✅ 已知长度或改用字符串比较函数。/* 适用于 C11 */if (strcmp(a, b) 0) { /* ... */ }❌ 认为std::string比较和strcmp对所有字节都一致。// 适用于 C17// 含 \0 的 std::string 与 C 字符串语义不同✅ 明确数据是否可能包含空字符跨 API 时统一表示。总结方法返回失败/边界适用strcmp负/0/正空指针 UB需\0C 字符串strncmp负/0/正最多 n 字符前缀、定长比较memcmp负/0/正需要长度二进制块std::string::operatorbool无内容相等std::string::compare负/0/正位置越界抛异常部分比较std::string_view负/0/正不拥有内存只读视图比较字符串的第一步不是选函数而是确认「这是 C 字符串还是std::string」「是否可能含空字符」「是否需要忽略大小写」。这三个问题答清楚剩下的只是调用哪个接口。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑