Static and dynamic arrays, multidimensional arrays, C-strings, std::string, string literal syntax, and modern alternatives.
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
Use an empty initializer list {} to zero-initialize every element:
bool flags[10] = {}; // all false
int nums[10] = {}; // all 0
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
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
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)
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
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.
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.std::stringstd::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
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*.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";
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"
The compiler automatically concatenates adjacent string literals at compile time — the cleaner modern approach:
std::string s = "Hello "
"World"; // "Hello World"
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 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
std::array when the size is fixed and known at compile time, std::vector when the size is dynamic.