Arrays, strings & C-style vs std::string

RoadmapsC++

Scenario

You try to concatenate two strings using the '+' operator. It works perfectly for std::string, but when you try it with "Hello" + "World", it fails to compile.

Why doesn't C++ treat string literals the same way it treats std::string?

Mental model

A C-style string is just a raw sequence of characters in memory that ends when it hits a 0. It's like reading until you hit a blank page. A std::string is a smart book that knows exactly how many pages it has and can dynamically bind more pages if needed.

C++ inherited C-style strings, which are simply arrays of characters ending with a null-terminator (`'\0'`). They have no bounds checking, don't know their own size, and cannot be compared or concatenated with normal operators. `std::string` is a modern C++ class that manages its own memory, knows its size in O(1) time, and supports intuitive operators like `+` and `==`.

Explanation

In C, strings are represented as arrays of `char`. Because arrays degrade to pointers when passed to functions, a C-style string loses its size information. The only way to know where it ends is the null-terminator (`'\0'`). Finding the length takes O(N) time (`strlen`). You cannot concatenate them with `+`; you must use `strcat` and manually manage buffer sizes, which is a massive source of buffer overflow vulnerabilities.

`std::string` is a C++ Standard Library class that acts as a wrapper around character data. It stores the length explicitly (O(1) `.length()`), automatically handles memory allocation/resizing, and overloads operators so `a == b` compares the contents (unlike C-strings, where `a == b` compares memory addresses).

String literals like `"Hello"` are statically allocated C-style arrays (`const char[6]`). To force a literal to be a `std::string`, use the `s` suffix (C++14): `"Hello"s`. You can also implicitly convert a C-string to a `std::string` by assignment.

Code examples

The nightmare of C-style strings

#include <cstring>
#include <iostream>

int main() {
    const char* a = "Hello";
    const char* b = "Hello";
    
    // WRONG: Compares pointer addresses, not string contents!
    if (a == b) { }
    
    // CORRECT but tedious:
    if (std::strcmp(a, b) == 0) { }
    
    char buffer[20] = "Hello ";
    // Manual length checking needed to prevent buffer overflows
    std::strcat(buffer, "World"); 
    return 0;
}

C-style strings are raw pointers. Operator `==` compares the addresses in memory. To compare contents, you must use `strcmp`. Modifying them requires manual buffer sizing.

The simplicity of std::string

#include <string>
#include <iostream>

int main() {
    std::string a = "Hello";
    std::string b = "Hello";
    
    // Works intuitively: compares contents.
    if (a == b) { 
        std::cout << "Equal\n";
    }
    
    // Dynamically resizes, no buffer overflow risk.
    std::string c = a + " World";
    
    // O(1) length lookup
    std::cout << "Length: " << c.length() << '\n';
    return 0;
}

`std::string` overloads operators to behave like primitive types. It abstracts away the memory management entirely.

Key points

Common mistakes

Recall questions

Questions & answers

If you need to return a substring from a function, why should you return a `std::string` instead of a `char*`?

Returning a `char*` usually means either returning a pointer to a local buffer (which goes out of scope and causes undefined behaviour) or returning dynamically allocated memory (forcing the caller to manually `delete[]` it). `std::string` handles its own memory lifecycle automatically via RAII.

Approach: Connect string management to memory lifecycle and dangling pointers.

A candidate uses `sizeof(str)` on a C-style string `char str[] = "Hello";` to find its length. What is wrong with this?

`sizeof(str)` returns the total allocated size of the array, which includes the null-terminator (so it returns 6, not 5). Furthermore, if `str` decays to a pointer, `sizeof(str)` returns the size of the pointer (usually 8 bytes), not the string length. `strlen()` or `std::string` should be used instead.

Approach: Understand the difference between array sizes and string lengths, and the decay to pointer.

Continue learning

Previous: Functions — pass by value, reference & pointer

Next: Preprocessor macros & #define for CP

Return to C++ Roadmap