C++ string vs char Array: Which Should You Use?
C++ has two ways to work with text: std::string (a modern class from the standard library) and char arrays (C-strings inherited from C). Understanding both is important — but choosing between them for new code is usually straightforward.
C-Strings: The Old Way
A C-string is a char array with a null terminator (\0) at the end:
char name[] = "Alice";
// Stored as: ['A', 'l', 'i', 'c', 'e', '\0']
// Length: 5 characters + 1 null terminator = 6 bytes
The null terminator marks where the string ends. Every C string function (strlen, strcpy, strcat) relies on it.
#include <cstring> // C string functions
char str1[] = "Hello";
char str2[20];
strcpy(str2, str1); // Copy str1 into str2
strcat(str2, " World"); // Append " World"
cout << strlen(str2); // 11
The problems with C-strings:
- Fixed size — you must allocate enough space manually
- Buffer overflow if you write past the end — no protection
- No automatic memory management
- Awkward to use (need
<cstring>functions, no operators)
std::string: The Modern Way
std::string is a class that manages the character data for you:
#include <string>
string name = "Alice";
name += " Smith"; // Concatenation with +=
name.append("!"); // Append method
cout << name.length(); // 12 — knows its own length
cout << name; // Alice Smith!
What std::string gives you:
- Grows automatically as needed — no size to pre-declare
- Knows its own length — no need to scan for
\0 - Operator overloading —
+,+=,==,<work naturally - Rich set of methods:
find(),substr(),replace(),erase(), etc. - Safe — no buffer overflow from basic operations
Side-by-Side Comparison
Concatenation
// C-string
char result[50];
strcpy(result, "Hello");
strcat(result, ", world"); // Dangerous if result is too small
// std::string
string result = "Hello";
result += ", world"; // Safe — grows automatically
Getting Length
// C-string
char str[] = "Hello";
int len = strlen(str); // Scans until \0 — O(n)
// std::string
string s = "Hello";
int len = s.length(); // O(1) — stored internally
Comparison
// C-string
char a[] = "apple";
char b[] = "apple";
if (strcmp(a, b) == 0) { ... } // Must use strcmp — == compares pointers!
// std::string
string a = "apple";
string b = "apple";
if (a == b) { ... } // == compares content naturally
Copying
// C-string
char src[] = "Hello";
char dest[10];
strcpy(dest, src); // Undefined behavior if dest is too small
// std::string
string src = "Hello";
string dest = src; // Safe copy — string manages its own memory
Finding a Substring
// C-string
char str[] = "Hello world";
char* found = strstr(str, "world");
if (found) cout << "Found at position: " << (found - str);
// std::string
string str = "Hello world";
size_t pos = str.find("world");
if (pos != string::npos) cout << "Found at position: " << pos;
Converting Between Them
std::string → char array:
string s = "Hello";
const char* cstr = s.c_str(); // Pointer to null-terminated C-string
// Valid as long as s is not modified
char buf[50];
strcpy(buf, s.c_str()); // Copy into a char array
char array → std::string:
char cstr[] = "Hello";
string s = cstr; // Direct construction
string s2(cstr, 3); // Take first 3 chars: "Hel"
When to Use Each
Use std::string for:
- All general-purpose string handling in C++ code
- Any code where you don’t know the string length in advance
- When you need operations like find, replace, split, format
- Anywhere safety and readability matter
Use char arrays for:
- Interfacing with C APIs that require
const char*orchar* - Embedded/systems code where dynamic allocation is forbidden
- Fixed-size buffers where performance is critical and size is known
- Legacy code maintenance
// C API requires char* — use c_str() to bridge
std::string filename = "data.txt";
FILE* f = fopen(filename.c_str(), "r"); // c_str() gives const char*
Common Mistakes
Comparing C-strings with ==:
char a[] = "hello";
char b[] = "hello";
if (a == b) { ... } // Compares pointer addresses — almost always false!
// Use: strcmp(a, b) == 0
Writing past the end of a char array:
char buf[5];
strcpy(buf, "Hello World"); // Buffer overflow — writes beyond buf!
// Use std::string instead, or check size first
Returning a pointer to a local char array:
const char* getDangerousString() {
char local[] = "Hello";
return local; // Dangling pointer — local is destroyed on return
}
// With std::string, this is fine:
string getSafeString() {
return "Hello"; // Returns a copy — safe
}
Summary
| Feature | char array | std::string |
|---|---|---|
| Size | Fixed at compile time | Dynamic, grows automatically |
| Length tracking | Manual (or strlen) | Automatic (.length()) |
| Concatenation | strcat (error-prone) | + or += (safe) |
| Comparison | strcmp | ==, <, > |
| Copying | strcpy | = assignment |
| Buffer overflow risk | Yes | No |
| C API compatibility | Native | Via .c_str() |
| Ease of use | Low | High |
Recommendation: Use std::string for all new C++ code. It’s safer, easier, and just as fast for most purposes.
Related Articles
- C++ String Handling — std::string methods, string_view, performance tips
- C++ int to string Conversion — converting numbers to strings
- C++ Arrays Tutorial — char arrays in the context of arrays
- C++ Variables and Data Types — char type basics
Take Your C++ Further
If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.