C++标准库中的std::string类提供了两个命名不同但行为完全一致的成员函数:length()和size()。它们都返回字符串中CharT类型元素的数量,对于std::string即char元素的数量,也就是字节数。这个结论不是某个平台或编译器特有的表现,而是ISO C++标准明确规定的。理解这一点之后,关于字符串长度获取的很多疑问都可以迎刃而解。

一、length与size在标准中的定义与实现细节
在C++标准中,std::basic_string模板类的size和length声明并列出现。以C++17标准草案为例,两者的返回类型都是size_type,且都带noexcept说明,复杂度为常数时间。它们不修改对象,也不会抛出异常。对于char实例化的std::string,size_type通常定义为std::allocator_traits<allocator>::size_type,在大多数实现中就是std::size_t。
为什么会有两个名字?这要从历史沿革说起。Standard Template Library(STL)在标准化过程中要求所有容器都提供size函数来表达元素个数,而早期的字符串类通常使用length来获取字符数。为了同时兼容两种习惯,std::string保留了length作为size的同义词。实际开发中,length和size没有任何性能差异,编译器通常会内联这些简单函数,生成的机器指令完全一致。
// libstdc++ basic_string 中 length 与 size 的简化实现
size_type
length() const _GLIBCXX_NOEXCEPT
{
return _M_rep()->_M_length;
}
size_type
size() const _GLIBCXX_NOEXCEPT
{
return _M_rep()->_M_length;
}
从上面的源码片段可以看到,两个函数返回的是同一个内部长度字段。即便不同编译器实现细节不同,标准保证两者的返回值、返回类型和异常行为完全一致,因此开发者在选择时只需考虑代码风格和团队规范,不需要担心语义差异。
二、与strlen和sizeof的对比:C风格长度获取的陷阱
C语言中获取字符串长度主要依赖strlen函数,它从指针位置开始向后扫描,直到碰到第一个空字符\0为止。这种机制决定了strlen的时间复杂度是O(n),并且如果字符串中间嵌入了空字符,strlen会提前返回,无法得到真实存储长度。std::string的length()和size()则在对象内部维护长度字段,时间复杂度为O(1),且对嵌入空字符不敏感。
#include <iostream>
#include <cstring>
#include <string>
int main()
{
// 只使用 const char* 构造,遇到第一个 \0 就停止
std::string s1("hello\0world");
// 显式指定长度 11,可以包含中间的空字符
std::string s2("hello\0world", 11);
std::cout << "s1.length() = " << s1.length() << '\n';
std::cout << "s1.size() = " << s1.size() << '\n';
std::cout << "s2.length() = " << s2.length() << '\n';
std::cout << "strlen(s2.c_str()) = " << strlen(s2.c_str()) << '\n';
return 0;
}
输出结果是:s1的length和size都是5,因为构造函数调用traits::length扫描到第一个空字符就停止;s2显式传入长度11,因此length和size都返回11,但strlen(s2.c_str())只返回5,因为内部第6个字节是空字符。这说明strlen不适合处理含有嵌入空字符的数据,而std::string的length()和size()可以准确反映存储的字节数。
sizeof是另一个容易混淆的运算符。它用于获取对象或类型在内存中的大小,不适用于获取字符串元素个数。例如char arr[] = "hello";中的sizeof(arr)返回6,因为编译器会为结尾空字符分配空间;而std::string对象本身的大小通常是固定的,例如在64位Linux上GCC的std::string对象大小为32字节,这个值不包含堆上实际存储的字符数据。因此,sizeof只能用于栈数组,不能用于动态分配的内存或std::string对象来获取长度。
#include <iostream>
#include <cstring>
#include <string>
int main()
{
char arr[] = "hello";
std::string st = "hello";
std::cout << "sizeof(arr) = " << sizeof(arr) << '\n'; // 6
std::cout << "strlen(arr) = " << strlen(arr) << '\n'; // 5
std::cout << "sizeof(st) = " << sizeof(st) << '\n'; // 通常为 32
std::cout << "st.length() = " << st.length() << '\n'; // 5
return 0;
}
三、多字节编码下长度与字符数的差异
std::string本质上是字节序列,它并不感知编码方式。对于UTF-8编码的中文文本,一个汉字通常由3个字节组成,此时length()和size()返回的是字节数6,而不是视觉字符数2。如果开发者直接用length去截断字符串,很可能把多字节字符从中间切开,产生乱码。因此,在处理多语言文本时需要区分字节长度和字符个数。
对于需要按字符截断的场景,可以编写UTF-8字符计数函数。基本原理是遍历字节序列,判断每个字节是否为UTF-8的前导字节。前导字节的特征是:最高位为0表示单字节ASCII字符,或者以110、1110、11110开头表示多字节字符的起始字节,以10开头的字节是后续字节,不计为新字符。以下代码给出一个简单实现。
#include <iostream>
#include <string>
std::size_t utf8_char_count(const std::string& s)
{
std::size_t count = 0;
for (unsigned char c : s) {
// 如果最高两位不是10,说明是一个新字符的起始字节
if ((c & 0xC0) != 0x80) {
++count;
}
}
return count;
}
int main()
{
std::string zh = "你好";
std::string mixed = "abc你好";
std::cout << "zh bytes: " << zh.size() << '\n'; // 6
std::cout << "zh chars: " << utf8_char_count(zh) << '\n'; // 2
std::cout << "mixed bytes: " << mixed.length() << '\n'; // 9
std::cout << "mixed chars: " << utf8_char_count(mixed) << '\n'; // 5
return 0;
}
即使使用std::wstring,其length()返回的是wchar_t元素个数,也不一定等于Unicode码点个数。因为wchar_t在Windows平台上通常为16位,遇到增补平面字符(如emoji)会使用代理对,两个wchar_t表示一个码点;在Linux上wchar_t为32位,才能直接表示一个码点。C++20引入std::u8string,但size()仍然返回码元个数,即char8_t的个数。因此,任何编码下都需要根据具体需求判断是要码元长度还是字符数。
四、capacity、reserve与长度获取的最佳实践
std::string还提供了capacity()函数,它表示当前分配的内存能够容纳的字符数,不包含结尾空字符。size()和length()返回的是实际字符数,capacity()通常大于或等于size()。reserve()操作可以预先分配内存,提升追加操作的效率,但它不会改变size()或length()。例如默认构造的string长度为0,调用reserve(100)后长度仍为0,容量至少为100。
#include <iostream>
#include <string>
int main()
{
std::string s;
std::cout << "initial size=" << s.size()
<< " capacity=" << s.capacity() << '\n';
s.reserve(100);
std::cout << "after reserve size=" << s.size()
<< " capacity=" << s.capacity() << '\n';
s = "hello";
std::cout << "after assign size=" << s.length()
<< " capacity=" << s.capacity() << '\n';
return 0;
}
在项目实践中,如果只关心字符串包含多少个char元素,length()和size()可以随意选择,但建议团队内部统一。倾向容器风格的开发者通常使用size(),而倾向字符串语义的开发者习惯使用length(),二者都不会影响性能。需要特别注意的是,不要在循环条件中频繁调用strlen,比如for (size_t i = 0; i < strlen(cstr); ++i)会导致每次迭代都扫描一遍字符串,时间复杂度退化为O(n²)。对于std::string,可以安全地写for (size_t i = 0; i < s.size(); ++i),因为size()是常数时间操作。
总结来看,length()与size()没有任何区别,只是历史兼容留下的两个名字。需要区分的是字节长度、字符个数、容量和对象大小这些概念。在涉及多字节编码、嵌入空字符或性能敏感场景时,选择合适的方法可以避免隐蔽的错误。本文的辨析同样适用于std::u8string、std::u16string和std::wstring等basic_string的实例化类型。
C++ string lengthsize区别字符串长度获取修改时间:2026-09-26 14:20:28