Standard Library[lib]
C++ string length: size(), length() and strlen
- Section
- Standard Library [lib]
- Standard
- C++17
- Compilers
- MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
- Published
- Length
- 6 min read
In this answer6 clauses
Abstract
Call size() or length() on a std::string: both return its number of characters, in constant time. For a C string use std::strlen, which counts up to the terminating null; sizeof gives an array’s size in bytes, the null and any unused space included. The length is unsigned, so size() - 1 on an empty string wraps around to a huge number; since C++20, std::ssize gives a signed length.

To get a C++ string length, call length() or size() on a std::string: both return the same count of characters. For a C string, text in a char array that ends in a null character, use std::strlen from <cstring>. sizeof is not a length: on an array it also counts the null and any unused space.
#include <iostream>
#include <string>
int main() {
std::string name = "Ada Lovelace";
std::cout << name.length() << '\n'; // 12
std::cout << name.size() << '\n'; // 12
}— end example]
It printed:
12
12— end example]
This answer is written for C++17; the one line that needs C++20 says so. The samples were built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four printed what the comments say unless a sentence says otherwise.
length() or size(): which one should you use?
Use either: size() and length() return the same number, in constant time, so the call costs the same for 12 characters as for a million (std::basic_string::size; the standard’s wording is in [string.capacity]). size() is the name std::vector and most other containers use; length() reads more naturally for text. To ask whether the length is 0, call empty(): the standard defines it as size() == 0, and it says what you mean.
std::cout << std::boolalpha;
std::string name = "Ada Lovelace";
std::cout << (name.size() == name.length()) << '\n'; // true
std::string nickname; // empty
std::cout << nickname.size() << '\n'; // 0
std::cout << nickname.empty() << '\n'; // true
name += ", Countess of Lovelace";
std::cout << name.length() << '\n'; // 34— end example]
length() counts every character, spaces included, so if a string read with std::cin >> s comes out short, >> stopped at the first space; reading a whole line with std::getline fixes that. If std::string itself is new to you, how a C++ string works covers creating, joining and searching one.
How do you get the length of a C string or a char array?
Call std::strlen, from <cstring>: it counts the characters before the terminating null, the zero byte that marks where a C string ends, walking the text to find it (std::strlen). sizeof answers a different question, the size of the array in bytes, so it counts the null and the unused space too:
std::cout << std::strlen("hello") << '\n'; // 5
char buffer[16] = "hello";
std::cout << std::strlen(buffer) << '\n'; // 5: up to the null
std::cout << sizeof(buffer) << '\n'; // 16: the whole array
std::cout << std::size(buffer) << '\n'; // 16: elements, not text
std::cout << sizeof("hello") << '\n'; // 6: the null counts— end example]

A string literal such as "hello" is an array of its characters plus the null, so sizeof("hello") is 6 and the literal’s length is sizeof("hello") - 1. std::size counts an array’s elements, and a char is one byte, so here it agrees with sizeof. Both work only on the array itself: on a const char* pointer, sizeof gives the size of the pointer and std::size does not compile. The C++ array answer shows why an array passed to a function becomes such a pointer.
strlen relies on the null: on an array without one, the behavior is undefined, which means the program may do anything.
Why is the length unsigned, and what goes wrong with size() - 1?
The length is unsigned: size() returns a std::size_t, a whole-number type that cannot be negative, so text.size() - 1 on an empty string is not -1 but wraps around to the largest value the type can hold. Comparing the length with a negative int goes wrong the same way, because the int is converted to unsigned first.
std::string text; // empty
std::cout << text.size() - 1 << '\n'; // not -1
int limit = -1;
if (limit < text.size()) { // limit is converted to unsigned
std::cout << "-1 is less than 0\n";
} else {
std::cout << "-1 is not less than 0\n"; // printed
}
int doubled = 0;
for (std::size_t i = 0; i + 1 < text.size(); ++i) {
if (text[i] == text[i + 1]) { // both exist here
++doubled;
}
}
std::cout << doubled << '\n'; // 0: the loop never runs— end example]
On these 64-bit builds it printed:
18446744073709551615
-1 is not less than 0
0— end example]
The first line is 2 to the power 64, minus 1.
The loop shows the fix: keep the subtraction out of the condition. Written as i < text.size() - 1, it would compare i with that huge number and read past the end of an empty string; i + 1 < text.size() is false at once.
None of the four compilers warned about size() - 1, printed or in a loop condition. GCC and Clang warned about the comparison (-Wsign-compare, with -Wall -Wextra) and MSVC at /W4 printed nothing, so you cannot count on a warning. GCC and Clang give the same warning for the everyday loop for (int i = 0; i < s.size(); ++i), and MSVC at /W4 again does not; declare i as a std::size_t, as the loop above does, or use a range-for, for (char c : s).
How do you get a signed length?
Since C++20, call std::ssize, declared in <iterator>: it returns a signed number, so std::ssize(text) - 1 on an empty string is -1, as you would expect (std::ssize). Before C++20, convert once with static_cast<int>, for strings you know are short enough for an int:
std::string text; // empty
std::cout << std::ssize(text) - 1 << '\n'; // C++20: -1
std::string name = "Ada Lovelace";
std::cout << static_cast<int>(name.size()) << '\n'; // 12— end example]
Built as C++17, the std::ssize line did not compile on any of the four; GCC said 'ssize' is not a member of 'std'; did you mean 'size'?. The cast compiles as C++17. The flag for C++20 is -std=c++20 for GCC and Clang and /std:c++20 for MSVC, and Since When’s C++20 table gives the first releases with its features.
Does the length count bytes or characters?
It counts chars, which are bytes, not the letters you see: for UTF-8 text, size() is the length in bytes. An accented letter such as ï takes two bytes in UTF-8, so the length comes out larger than the number of letters:
std::string word = "na\xC3\xAFve"; // "naive" with i-diaeresis, UTF-8
std::cout << word.size() << '\n'; // 6 bytes
int letters = 0;
for (char c : word) {
// a byte 10xxxxxx continues the character before it
if ((static_cast<unsigned char>(c) & 0xC0) != 0x80) {
++letters;
}
}
std::cout << letters << '\n'; // 5 letters— end example]
The loop counts code points, the numbers Unicode gives its characters: the bytes that start one, skipping the ones that continue it. A letter written as two code points, such as an i followed by a combining diaeresis, counts as two. Other scripts take more bytes a character: in cppreference’s example, a Japanese string of eight characters has a size() of 24.
Which length function for which string?
| You have | Write | It gives |
|---|---|---|
a std::string s |
s.size() or s.length() |
its characters |
a C string or char array p |
std::strlen(p) |
the characters before the null |
the room in a char array buffer |
sizeof(buffer) or std::size(buffer) |
every cell, null and unused space included |
| a string literal | sizeof("hello") - 1 |
its characters, here 5 |
| a length you subtract from | std::ssize(s) |
a signed length (C++20) |
| a question: is it empty? | s.empty() |
true when the length is 0 |
Standard library has the other answers on strings and containers, such as comparing two strings.
Questions
Does length() count the null terminator?
No. size() and length() count the characters only: “Ada Lovelace” has a length of 12. std::strlen stops before the null too, so strlen(“hello”) is 5; sizeof(“hello”) measures the array, null included, and gives 6.
Is std::string length() O(1)?
Yes. Since C++11 the standard requires size() and length() to take constant time, so the call costs the same for any length of string. std::strlen is different: it walks a C string until it finds the null, so its time grows with the text.
What is the difference between size() and capacity()?
size() is the number of characters the string holds. capacity() is the room it has reserved, which it fills before it must grow. For the length, use size().
What does sizeof(std::string) return?
The size of the string object, which the library and the build fix: 24 to 40 bytes on these compilers, the same for a 12-character string as for a 1,000-character one. It is never the length of the text; use size() for that.
How do you get the length of a string with a null character inside it?
Call size(): std::string two(“a\0b”, 3) holds three characters and its size() is 3, while std::strlen(two.c_str()) is 1, because strlen stops at the first null. Pass the count when you build such a string: std::string cut(“a\0b”) has a size() of 1, since without it the constructor also stops at the null.
What is the maximum length of a std::string?
max_size() returns it: the most characters a string can hold, given the system’s and the library’s limits. On a 64-bit build the number is far larger than the memory a program can get, so memory runs out first.
Sources
- std::basic_string::size, std::basic_string::lengthcppreference.com
- [string.capacity] CapacityC++ standard working draft
- std::strlencppreference.com
- String literalcppreference.com
- std::size, std::ssizecppreference.com
- sizeof operatorcppreference.com
- std::basic_string::max_sizecppreference.com