Contiguous memory sequencing and string semantics in C++: 1D, 2D, and multidimensional arrays, Row-Major Order memory offsets, matrix algorithms, array-to-pointer decay, fundamental searching and sorting (Linear, Binary, Insertion), C-style null-terminated strings (<cstring>), and the modern dynamic std::string container (SSO, capacity, manipulation, and comparisons).
An array is a fixed-size, homogeneous sequence of elements stored in contiguous memory addresses. Because elements are adjacent in physical RAM, indexing operates in instantaneous O(1) constant time using pointer arithmetic:
For an array declared as T arr[N];, the memory address of element arr[i] is computed as:
Address(arr[i]) = Base_Address + (i × sizeof(T))
int scores[5] = {90, 85, 78, 92, 88};0 to N - 1.scores[10] writes to out-of-bounds stack memory, causing buffer overflows or segmentation faults.C++ stores 2D arrays in Row-Major Order (entire row 0, followed by row 1, etc., in a continuous linear memory block):
For int mat[ROWS][COLS];:
Address(mat[i][j]) = Base + ((i × COLS) + j) × sizeof(int)
mat[2][3][10, 20, 30] | Row 1: [40, 50, 60]0x100: [10] → 0x104: [20] → 0x108: [30] → 0x10C: [40] → 0x110: [50] → 0x114: [60]
When a raw array is passed into a function, it undergoes Array-to-Pointer Decay: the array parameter implicitly decays into a pointer pointing to its first element (int*). Consequently, the function loses all knowledge of the array's size:
#include <iostream>
// Raw Array Passing: Must explicitly pass size!
// 'int arr[]' decays to 'int* arr'
void printArray(const int arr[], size_t size) {
for (size_t i = 0; i < size; ++i) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
}
int main() {
int data[] = {12, 24, 36, 48, 60};
size_t length = sizeof(data) / sizeof(data[0]);
// 1. Traditional Index-Based Traversal
printArray(data, length);
// 2. Modern C++11 Range-Based For Loop (Value Copy)
std::cout << "Range-based for: ";
for (int value : data) {
std::cout << value << " ";
}
std::cout << "\n";
// 3. Range-Based For by Const Reference (Zero Copy)
for (const auto& val : data) {
// Read-only access to val
}
return 0;
}
sizeof(arr) Inside Function Trap
Inside main(), sizeof(data) evaluates to 20 bytes (5 integers × 4 bytes). But inside printArray(const int arr[]), sizeof(arr) evaluates to 8 bytes (the size of a 64-bit pointer address)! Never attempt sizeof(arr) / sizeof(arr[0]) inside a function receiving a decayed array.
Iterates through elements sequentially from index 0 to N-1 until a match is found. Works on unsorted arrays. Worst-case: N comparisons.
Requires the array to be pre-sorted. Compares target with middle element, repeatedly halving the search space. 1,000,000 elements searched in only ~20 comparisons!
#include <iostream>
// Binary Search Algorithm on a Sorted Array: O(log N)
int binarySearch(const int arr[], int size, int target) {
int low = 0;
int high = size - 1;
while (low <= high) {
// Prevents integer overflow: (low + high) / 2 could overflow!
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid; // Target found at index mid
} else if (arr[mid] < target) {
low = mid + 1; // Target in right half
} else {
high = mid - 1; // Target in left half
}
}
return -1; // Target not found
}
// 2x2 Matrix Multiplication: C[i][j] = sum(A[i][k] * B[k][j])
void multiplyMatrix2x2(int A[2][2], int B[2][2], int C[2][2]) {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
C[i][j] = 0;
for (int k = 0; k < 2; ++k) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
}
std::string
C++ supports two distinct text paradigms: historical C-style character arrays and the modern, heap-managed std::string container class:
| Feature | C-Style String (char[] / const char*) |
Modern C++ std::string Class |
|---|---|---|
| Memory Model | Fixed-size array requiring manual null-terminator ('\0') |
Dynamic memory management; automatically resizes on demand |
| Length Tracking | Requires linear scan O(N) using strlen() |
Stored as internal field; retrieved in O(1) via .length() |
| Safety | High vulnerability to Buffer Overflow attacks | Memory-safe with boundary checks (.at()) |
| Copy & Assign | Requires strcpy(); assignment operator illegal |
Native assignment operator (s1 = s2) with deep copy semantics |
| Optimization | Zero class overhead | Small String Optimization (SSO) avoids heap for short strings |
#include <iostream>
#include <cstring> // For C-style functions: strlen, strcpy, strcmp
#include <string> // For modern C++ std::string
int main() {
// 1. C-STYLE STRING: Must account for '\0' (Null Terminator)!
char cstr[10] = "Dev"; // Memory: ['D', 'e', 'v', '\0', ...]
std::cout << "C-string length: " << std::strlen(cstr) << "\n"; // 3
// 2. MODERN STD::STRING CLASS
std::string str1 = "DevAscent";
std::string str2 = " Academy";
// Concatenation
std::string fullCourse = str1 + str2; // "DevAscent Academy"
// String Substring and Search
std::string brand = fullCourse.substr(0, 9); // "DevAscent"
size_t foundIndex = fullCourse.find("Academy");
if (foundIndex != std::string::npos) {
std::cout << "Found 'Academy' at index: " << foundIndex << "\n";
}
return 0;
}
In modern C++ implementations (GCC libstdc++, Clang libc++, MSVC), std::string contains an internal stack buffer (typically 15 to 22 bytes). For short strings, the data resides directly on the stack, bypassing malloc/new heap allocations entirely. Only when strings exceed this capacity does std::string allocate dynamic heap memory.
cin vs getline()cin >> str;: Reads single token up to first whitespace character.std::getline(std::cin, str);: Reads an entire line including spaces up to \n.cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');: Flushes remaining newline after cin >>.C++ compares strings lexicographically based on ASCII/Unicode numeric values:
str1 == str2 (Equality check)str1 < str2 (Alphabetical sorting order)<=>): Yields std::strong_ordering::less, equal, or greater in a single comparison cycle.int[10] decays to int*). It occurs because C++ inherited C's memory efficiency model to avoid copying entire arrays onto the stack frame. As a consequence, array size information is lost and must be passed as an explicit companion parameter.
arr[2][3] in an int arr[4][5] starting at address 1000.
int of 4 bytes and COLS = 5:Offset = (row × COLS + col) = (2 × 5 + 3) = 13 elements.Address = 1000 + (13 × 4) = 1000 + 52 = 1052.
low + (high - low) / 2 instead of (low + high) / 2 in Binary Search?
low and high are large integers (e.g., in arrays with millions of elements where indices approach INT_MAX), adding low + high exceeds 2,147,483,647 and triggers a signed integer overflow (Undefined Behavior), resulting in a negative number and causing an immediate out-of-bounds crash. low + (high - low) / 2 mathematically avoids overflow.
std::string?
std::string uses an internal stack array (typically 15 to 22 bytes) to store short strings directly within the object itself. This eliminates the CPU latency and memory fragmentation associated with making a dynamic heap allocation (new/malloc) for small strings.
char*) considered hazardous compared to std::string?
'\0') to determine boundaries. If a string manipulation function (like strcpy or strcat) writes past the allocated array bounds, or if the null terminator is overwritten, operations read and write into adjacent memory uncontrollably. This is the root cause of classic Buffer Overflow security exploits.
std::array (introduced in C++11) improve upon raw C-style arrays?
std::array<T, N> is a zero-overhead container wrapping a fixed-size C-style array. Unlike raw arrays, std::array does not implicitly decay to a pointer when passed by value, knows its own size via .size(), provides safe bounds-checked element access via .at(i), supports copy/assignment syntax, and integrates natively with STL algorithms and iterators.
const auto&?
const auto& when iterating over elements of non-primitive types (such as an array of structs or std::string objects) to prevent creating an expensive local copy of each element on every iteration, while simultaneously guaranteeing that the loop body cannot accidentally mutate the original data.
strlen(cstr) and str.length()?
strlen(cstr) must iterate character-by-character through memory until it encounters the '\0' terminator, running in O(N) linear time. In contrast, std::string::length() returns a precomputed size member variable stored inside the class instance, running in O(1) constant time.