Chapter 04

Managing Arrays and Strings

Static and dynamic arrays, multidimensional arrays, C-strings, std::string, string literal syntax, and modern alternatives.

In this chapter

  1. Static Arrays
  2. Initializing Arrays
  3. Arrays and Pointers
  4. Multidimensional Arrays
  5. C-Style Character Strings
  6. std::string
  7. String Literal Syntax
  8. Modern Alternatives
1

Static Arrays

Static arrays have lengths that are known at compile time. Dynamic arrays can have differing lengths determined at runtime.

Declaring a static array:

int numbers[2] = {6, 10};
//  ^type  ^name  ^length  ^initial values
Uninitialized static arrays do not default to zero — they contain garbage values. Always initialize explicitly.

2

Initializing Arrays

Initialize All to Default

Use an empty initializer list {} to zero-initialize every element:

bool flags[10] = {};   // all false
int  nums[10]  = {};   // all 0

Partial Initialization

Specifying fewer values than the array length leaves the rest at the default for that type:

bool flags[10] = {false, true};  // [0]=false, [1]=true, [2..9]=false

Omitting the Size

If you provide a full initializer list you can omit the length — the compiler deduces it:

int primes[] = {2, 3, 5, 7, 11};  // length deduced as 5

3

Arrays and Pointers

An array name is a pointer to its first element. numbers[i] is syntactic sugar for *(numbers + i) — i.e. it adds i to the base memory address and dereferences.

int numbers[] = {6, 10};
// numbers    == address of numbers[0]
// numbers[0] == 6  (offset 0)
// numbers[1] == 10 (offset 1)
This is called array decay: when passed to a function, an array silently becomes a pointer. The size information is lost — you need to pass the length separately.

4

Multidimensional Arrays

Arrays can have more than one dimension. They are stored in row-major order in memory.

int grid[2][2] = {
    {1, 2},  // row 0
    {3, 4}   // row 1
};
// grid[0][1] == 2

5

C-Style Character Strings

C-strings are char arrays that use the null terminator character '\0' (value 0) to mark the end of the string.

char myString[] = "test";  // stored as {'t','e','s','t','\0'} — 5 bytes

The null terminator is added automatically when using string literals. This means a string of 4 characters occupies 5 bytes.

Many C functions like strlen() rely on finding the null terminator to know where the string ends. If a char array is not null-terminated, these functions will read past the end into undefined memory — a common source of bugs and security vulnerabilities.

Why C-Strings are Dangerous


6

std::string

std::string is the preferred and safer alternative to C-strings. It manages memory automatically and provides a rich set of methods.

#include <string>

std::string name = "Ada";
name += " Lovelace";           // concatenation

name.length();                  // 12
name.substr(4, 8);             // "Lovelace"
name.find("Love");              // 4 (index)
name.c_str();                   // const char* for C interop
Prefer std::string over char[] in all modern C++ code. Use c_str() only when you need to pass to a C API that expects a const char*.

7

String Literal Syntax

A string literal cannot span multiple source lines — splitting it causes a compiler error:

// ERROR — string literal cannot span lines
std::string s = "Hello
World";

Fix 1: Backslash Line Continuation

A backslash at the end of a line tells the preprocessor to join it with the next line:

std::string s = "Hello \
World";  // becomes "Hello World"

Fix 2: Adjacent String Literal Concatenation

The compiler automatically concatenates adjacent string literals at compile time — the cleaner modern approach:

std::string s = "Hello "
                "World";  // "Hello World"

+

Modern Alternatives

std::array (C++11) — Safer Static Array

std::array wraps a raw array with bounds-checking methods and doesn't decay to a pointer when passed to functions.

#include <array>

std::array<int, 5> nums = {1, 2, 3, 4, 5};
nums.size();        // 5 — size never lost
nums.at(2);         // 3 — throws std::out_of_range if out of bounds

std::vector — Dynamic Array

std::vector is the go-to dynamic array. It grows automatically and manages its own memory.

#include <vector>

std::vector<int> v = {1, 2, 3};
v.push_back(4);    // {1, 2, 3, 4}
v.size();           // 4
v[0];               // 1
The rule of thumb: use std::array when the size is fixed and known at compile time, std::vector when the size is dynamic.
← Chapter 3 ↑ Index Chapter 5 →