CppC-Style Strings

C-Style Strings

Before std::string existed, C++ inherited its string handling directly from C: a string is simply an array of characters terminated by a special null character, written '\0'. Understanding C-style strings is useful for reading older code and for interfacing with C libraries, even though modern C++ code should almost always prefer std::string.
Null-terminated character arrays
A string literal like "hello" is stored as an array of 6 characters: the 5 visible letters plus a hidden '\0' marking the end. Functions that operate on C-style strings scan forward until they hit that null terminator — there is no separate length stored anywhere.

cstring_basics.cpp

CPP
#include <iostream>

int main() {
    char str1[] = "hello";          // size automatically becomes 6 (5 + '\0')
    char str2[10] = "hi";           // explicit size, rest is zero-filled
    char str3[] = {'h', 'i', '\0'}; // manual construction, same as "hi"

    std::cout << str1 << std::endl;

    for (int i = 0; str1[i] != '\0'; i++) {
        std::cout << str1[i];
    }
    std::cout << std::endl;

    return 0;
}
The <cstring> function library
The <cstring> header provides free functions for common string operations, since C-style strings have no member functions of their own.

Function

Purpose

strlen(s)

Returns the length of s, not counting the null terminator

strcpy(dest, src)

Copies src into dest, including the null terminator

strcat(dest, src)

Appends src onto the end of dest

strcmp(a, b)

Returns 0 if equal, negative if a < b, positive if a > b

strncpy(dest, src, n)

Copies at most n characters from src into dest

cstring_functions.cpp

CPP
#include <cstring>
#include <iostream>

int main() {
    char greeting[20] = "Hello, ";
    char name[] = "World!";

    std::cout << "Length: " << strlen(greeting) << std::endl;

    strcat(greeting, name);   // greeting now holds "Hello, World!"
    std::cout << greeting << std::endl;

    if (strcmp(greeting, "Hello, World!") == 0) {
        std::cout << "Strings match!" << std::endl;
    }

    return 0;
}
strcpy and strcat are a historic source of security bugs
Neither strcpy nor strcat checks whether the destination buffer is actually large enough to hold the result. If the source string is longer than the destination array, the extra bytes overwrite whatever memory comes after the buffer — a buffer overflow. This single class of bug is responsible for an enormous number of real-world security vulnerabilities, including some of the most famous computer worms in history. Never use strcpy/strcat on a buffer whose size you have not personally verified is big enough.

buffer_overflow_danger.cpp

CPP
char small[6];
strcpy(small, "This string is way too long"); // undefined behavior: overflow!
strncpy — safer, but still tricky
strncpy takes a maximum length and will not write past it, which avoids the overflow above. However, it has its own sharp edge: if the source string is longer than or equal to the given length, strncpy does not null-terminate the result, leaving you with a string that never ends properly.

strncpy_caveat.cpp

CPP
char dest[6];
strncpy(dest, "HelloWorld", 5); // copies "Hello", but does NOT add '\0'
dest[5] = '\0';                 // you must terminate it yourself
strncpy is a safety net, not a fix
Always manually null-terminate the destination after calling strncpy if there is any chance the source was truncated.
Why modern C++ uses std::string instead
  • Automatic memory management — no fixed-size buffer to overflow, and no manual delete required.

  • Knows its own length — no scanning for a null terminator just to find the size.

  • Operator support — concatenate with +, compare with ==, all without calling separate functions.

  • Works seamlessly with the STL — streams, containers, and algorithms all understand it natively.

Up next
The next chapter covers std::string in depth — the string type you should reach for in essentially all new C++ code.