Arrays & Strings
Working with collections of data and text manipulation in C++
What You Will Learn
- How to declare and use C-style arrays
- Multi-dimensional arrays for matrices and grids
- C-style strings (char arrays)
- Modern std::string and its powerful methods
- String manipulation techniques
- String streams for parsing and formatting
- std::string_view for efficient string handling
- std::array for safe, fixed-size arrays
- Preview of std::vector
Arrays Introduction
An array is a fixed-size collection of elements of the same type, stored in contiguous memory.
Declaring Arrays
// Syntax: type name[size];
int numbers[5]; // Uninitialized (contains garbage)
int scores[5] = {0}; // All elements initialized to 0
int values[5] = {1, 2, 3, 4, 5}; // Explicit initialization
// Size can be omitted if initialized
int arr[] = {10, 20, 30}; // Size is 3
// Partial initialization (remaining elements are 0)
int data[10] = {1, 2, 3}; // data[3] to data[9] are 0
Accessing Elements
int arr[5] = {10, 20, 30, 40, 50};
// Access with index (0-based)
std::cout << arr[0]; // 10 (first element)
std::cout << arr[4]; // 50 (last element)
// Modify elements
arr[2] = 100; // arr is now {10, 20, 100, 40, 50}
// WARNING: No bounds checking!
// arr[5] = 60; // Undefined behavior! Out of bounds
Array Size
int arr[] = {1, 2, 3, 4, 5};
// Calculate size
int size = sizeof(arr) / sizeof(arr[0]); // 5
// With std::size (C++17)
#include <iterator>
int size2 = std::size(arr); // 5
// Note: This doesn't work with pointers
void printSize(int* arr) {
// sizeof(arr) returns pointer size, not array size!
}
Array Operations
Iterating Over Arrays
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
// Traditional for loop
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
// Range-based for loop (C++11)
for (int x : arr) {
std::cout << x << " ";
}
// Modify with reference
for (int& x : arr) {
x *= 2; // Doubles each element
}
Finding Elements
int arr[] = {10, 20, 30, 40, 50};
int size = 5;
int target = 30;
// Linear search
int index = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
index = i;
break;
}
}
// index = 2
// Using std::find
#include <algorithm>
auto it = std::find(std::begin(arr), std::end(arr), target);
if (it != std::end(arr)) {
std::cout << "Found at index: " << (it - std::begin(arr));
}
Sorting Arrays
#include <algorithm>
int arr[] = {5, 2, 8, 1, 9};
// Sort ascending
std::sort(std::begin(arr), std::end(arr));
// arr = {1, 2, 5, 8, 9}
// Sort descending
std::sort(std::begin(arr), std::end(arr), std::greater<int>());
// arr = {9, 8, 5, 2, 1}
// Custom comparison
std::sort(std::begin(arr), std::end(arr), [](int a, int b) {
return a > b; // Descending
});
Copying Arrays
int source[] = {1, 2, 3, 4, 5};
int dest[5];
// Method 1: Loop
for (int i = 0; i < 5; i++) {
dest[i] = source[i];
}
// Method 2: std::copy
#include <algorithm>
std::copy(std::begin(source), std::end(source), std::begin(dest));
// Method 3: memcpy (for POD types)
#include <cstring>
std::memcpy(dest, source, sizeof(source));
Filling Arrays
int arr[10];
// Fill with a value
std::fill(std::begin(arr), std::end(arr), 42);
// arr = {42, 42, 42, ...}
// Fill with memset (bytes only, works for 0 or -1)
std::memset(arr, 0, sizeof(arr));
// arr = {0, 0, 0, ...}
Multi-Dimensional Arrays
Arrays can have multiple dimensions for representing matrices, grids, and tables.
2D Arrays
// Syntax: type name[rows][cols];
int matrix[3][4]; // 3 rows, 4 columns
// Initialization
int grid[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Access elements
std::cout << grid[0][0]; // 1 (top-left)
std::cout << grid[2][2]; // 9 (bottom-right)
grid[1][1] = 100; // Modify center element
Iterating 2D Arrays
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Nested loops
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << "\n";
}
// Range-based (C++11)
for (const auto& row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << "\n";
}
3D Arrays
// 3D array: depth x rows x cols
int cube[2][3][4];
// Access
cube[0][1][2] = 42;
// Useful for:
// - RGB images (height x width x 3)
// - 3D game worlds
// - Time series of matrices
Passing 2D Arrays to Functions
// Must specify all dimensions except first
void print2D(int arr[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << "\n";
}
}
// With templates (more flexible)
template <size_t R, size_t C>
void print2D(int (&arr)[R][C]) {
for (size_t i = 0; i < R; i++) {
for (size_t j = 0; j < C; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << "\n";
}
}
Matrix Operations
// Matrix addition
void addMatrices(int a[][3], int b[][3], int result[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
result[i][j] = a[i][j] + b[i][j];
}
}
}
// Matrix multiplication
void multiplyMatrices(int a[][3], int b[][3], int result[][3],
int r1, int c1, int c2) {
for (int i = 0; i < r1; i++) {
for (int j = 0; j < c2; j++) {
result[i][j] = 0;
for (int k = 0; k < c1; k++) {
result[i][j] += a[i][k] * b[k][j];
}
}
}
}
C-Style Strings
C-style strings are character arrays terminated with a null character '\0'.
Declaring C-Strings
// String literal (null-terminated automatically)
char greeting[] = "Hello"; // Size is 6 (includes '\0')
// Character array
char name[20] = "World"; // Extra space for longer names
// As char pointer (read-only string literal)
const char* message = "Hello, World!";
C-String Functions (<cstring>)
#include <cstring>
char str1[50] = "Hello";
char str2[] = " World";
// strlen - get length (excluding '\0')
size_t len = strlen(str1); // 5
// strcpy - copy string
char dest[50];
strcpy(dest, str1); // dest = "Hello"
// strcat - concatenate
strcat(str1, str2); // str1 = "Hello World"
// strcmp - compare strings
int result = strcmp("abc", "abd"); // < 0 (a < b)
// strncpy, strncat - safer versions with length limit
strncpy(dest, str1, 10);
strncat(dest, str2, 10);
Reading C-Strings
char name[100];
// Using cin (stops at whitespace)
std::cin >> name; // "John Doe" only reads "John"
// Using getline
std::cin.getline(name, 100); // Reads entire line
// Gets deprecated - use fgets or getline
// gets(name); // Dangerous! No bounds checking
C-String Dangers
- Buffer overflow if string exceeds array size
- No automatic memory management
- Missing null terminator causes undefined behavior
- Prefer
std::stringin modern C++
std::string
std::string is the modern, safe way to work with strings in C++.
Creating Strings
#include <string>
std::string s1; // Empty string
std::string s2 = "Hello"; // From string literal
std::string s3("World"); // Constructor syntax
std::string s4(5, 'a'); // "aaaaa" (repeat character)
std::string s5 = s2; // Copy
std::string s6 = s2 + " " + s3; // Concatenation
// From C-string
const char* cstr = "Hello";
std::string s7 = cstr;
std::string s8(cstr);
Basic Operations
std::string s = "Hello, World!";
// Length
size_t len = s.length(); // 13
size_t len2 = s.size(); // Same as length()
// Empty check
bool empty = s.empty(); // false
// Access characters
char first = s[0]; // 'H'
char last = s.back(); // '!'
char safe = s.at(0); // 'H' (with bounds checking)
// Modify
s[0] = 'h'; // "hello, World!"
s.front() = 'H'; // "Hello, World!"
// Concatenation
std::string a = "Hello";
a += " World"; // "Hello World"
a.append("!"); // "Hello World!"
// Clear
s.clear(); // s is now ""
Comparing Strings
std::string a = "apple";
std::string b = "banana";
// Comparison operators
bool eq = (a == b); // false
bool lt = (a < b); // true (lexicographic)
bool ne = (a != b); // true
// compare() method
int cmp = a.compare(b); // < 0 because a < b
// Case-insensitive comparison (no built-in, use algorithm)
#include <algorithm>
#include <cctype>
bool equalsIgnoreCase(const std::string& a, const std::string& b) {
return a.size() == b.size() &&
std::equal(a.begin(), a.end(), b.begin(),
[](char c1, char c2) {
return std::tolower(c1) == std::tolower(c2);
});
}
Input/Output
std::string name;
// Read word (stops at whitespace)
std::cin >> name;
// Read entire line
std::getline(std::cin, name);
// Read with custom delimiter
std::getline(std::cin, name, ',');
// After cin, clear newline before getline
std::cin >> age;
std::cin.ignore(); // Skip newline
std::getline(std::cin, address);
// Output
std::cout << name << "\n";
String Methods
Searching
std::string s = "Hello, World!";
// Find substring (returns position or npos if not found)
size_t pos = s.find("World"); // 7
size_t pos2 = s.find("xyz"); // std::string::npos
if (s.find("Hello") != std::string::npos) {
std::cout << "Found!\n";
}
// Find from position
size_t pos3 = s.find("o", 5); // 8 (second 'o')
// Find last occurrence
size_t pos4 = s.rfind("o"); // 8
// Find any character from set
size_t pos5 = s.find_first_of("aeiou"); // 1 ('e')
size_t pos6 = s.find_last_of("aeiou"); // 8 ('o')
// Find character not in set
size_t pos7 = s.find_first_not_of(" "); // First non-space
Substrings
std::string s = "Hello, World!";
// substr(pos, len) - extract substring
std::string sub = s.substr(7, 5); // "World"
std::string sub2 = s.substr(7); // "World!" (to end)
// Get prefix/suffix
std::string prefix = s.substr(0, 5); // "Hello"
Modifying Strings
std::string s = "Hello, World!";
// Replace
s.replace(7, 5, "C++"); // "Hello, C++!"
s.replace(0, 5, "Hi"); // "Hi, C++!"
// Insert
s.insert(3, "there "); // "Hi,there C++!"
// Erase
s.erase(3, 6); // "Hi, C++!"
s.erase(0, 4); // "C++!"
// Pop back
s.pop_back(); // "C++"
// Resize
s.resize(10, '!'); // "C++!!!!!!!"
s.resize(3); // "C++"
Useful Transformations
#include <algorithm>
#include <cctype>
std::string s = "Hello, World!";
// To uppercase
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
// "HELLO, WORLD!"
// To lowercase
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
// "hello, world!"
// Reverse
std::reverse(s.begin(), s.end());
// "!dlrow ,olleh"
// Trim whitespace (no built-in)
std::string trim(const std::string& s) {
size_t start = s.find_first_not_of(" \t\n\r");
size_t end = s.find_last_not_of(" \t\n\r");
if (start == std::string::npos) return "";
return s.substr(start, end - start + 1);
}
Splitting Strings
// Split by delimiter
std::vector<std::string> split(const std::string& s, char delim) {
std::vector<std::string> tokens;
std::stringstream ss(s);
std::string token;
while (std::getline(ss, token, delim)) {
tokens.push_back(token);
}
return tokens;
}
auto parts = split("apple,banana,cherry", ',');
// {"apple", "banana", "cherry"}
// Using find and substr
std::vector<std::string> split2(const std::string& s,
const std::string& delim) {
std::vector<std::string> tokens;
size_t start = 0, end;
while ((end = s.find(delim, start)) != std::string::npos) {
tokens.push_back(s.substr(start, end - start));
start = end + delim.length();
}
tokens.push_back(s.substr(start));
return tokens;
}
Conversion
// String to number
std::string numStr = "42";
int num = std::stoi(numStr); // 42
long numL = std::stol(numStr); // 42L
double numD = std::stod("3.14"); // 3.14
float numF = std::stof("2.5"); // 2.5f
// Number to string
std::string s1 = std::to_string(42); // "42"
std::string s2 = std::to_string(3.14159); // "3.141590"
// C-string conversion
std::string str = "Hello";
const char* cstr = str.c_str(); // Get C-string
const char* data = str.data(); // Same in C++11+
String Streams
std::stringstream allows treating strings like streams for parsing and formatting.
#include <sstream>
// Parsing with stringstream
std::string data = "42 3.14 Hello";
std::stringstream ss(data);
int num;
double pi;
std::string word;
ss >> num >> pi >> word;
// num = 42, pi = 3.14, word = "Hello"
// Building strings
std::stringstream builder;
builder << "Value: " << 42 << ", Pi: " << std::fixed
<< std::setprecision(2) << 3.14159;
std::string result = builder.str();
// "Value: 42, Pi: 3.14"
// Clear and reuse
builder.str(""); // Clear contents
builder.clear(); // Clear error flags
Input/Output String Streams
// Input only
std::istringstream iss("10 20 30");
int a, b, c;
iss >> a >> b >> c;
// Output only
std::ostringstream oss;
oss << "Hello" << " " << "World";
std::string result = oss.str();
Parsing CSV
std::string csv = "John,25,NYC";
std::stringstream ss(csv);
std::string name, city;
int age;
std::getline(ss, name, ',');
ss >> age;
ss.ignore(); // Skip comma
std::getline(ss, city, ',');
// name = "John", age = 25, city = "NYC"
std::string_view (C++17)
std::string_view is a non-owning reference to a string. It avoids copying.
#include <string_view>
void print(std::string_view sv) {
std::cout << sv << "\n";
}
// Works with different string types without copying
print("Hello"); // String literal
print(std::string("World")); // std::string
print(std::string_view("Hi")); // Another string_view
// Creating string_view
std::string str = "Hello, World!";
std::string_view sv1 = str; // View of entire string
std::string_view sv2(str.data(), 5); // "Hello"
std::string_view sv3 = str.substr(0, 5); // Also "Hello"
// Useful methods (similar to string)
sv1.size();
sv1.substr(0, 5);
sv1.find("World");
sv1[0];
sv1.remove_prefix(7); // Now views "World!"
sv1.remove_suffix(1); // Now views "World"
string_view Pitfalls
- The underlying string must outlive the view
- Don't return string_view to local strings
- No null termination guaranteed
// Dangerous!
std::string_view bad() {
std::string local = "Hello";
return local; // local destroyed, view dangles!
}
std::array (C++11)
std::array is a fixed-size container that wraps C-style arrays with added safety.
#include <array>
// Declaration
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Access
arr[0]; // No bounds checking
arr.at(0); // With bounds checking (throws if out of range)
arr.front(); // First element
arr.back(); // Last element
arr.data(); // Pointer to underlying array
// Size (always known)
arr.size(); // 5
arr.empty(); // false
arr.max_size(); // 5
// Iteration
for (int x : arr) {
std::cout << x << " ";
}
// Fill
arr.fill(0); // All elements become 0
// Swap
std::array<int, 5> arr2 = {10, 20, 30, 40, 50};
arr.swap(arr2);
// Comparison
if (arr == arr2) { /* ... */ }
if (arr < arr2) { /* ... */ } // Lexicographic
Benefits Over C-Arrays
- Knows its own size (
.size()) - Bounds checking with
.at() - Can be passed by value/reference without decay
- Works with STL algorithms
- Supports comparison operators
// C-array decays to pointer when passed
void func(int arr[]); // Actually takes int*
// std::array doesn't decay
void func(std::array<int, 5> arr); // Receives copy
void func(const std::array<int, 5>& arr); // Reference
std::vector Preview
std::vector is a dynamic array that can grow and shrink. It's covered in depth in the STL chapter.
#include <vector>
// Dynamic size
std::vector<int> v; // Empty
std::vector<int> v2 = {1, 2, 3}; // With elements
std::vector<int> v3(10); // 10 zeros
std::vector<int> v4(10, 5); // 10 fives
// Add elements
v.push_back(42); // Add to end
v.emplace_back(100); // Construct in place
// Access
v[0];
v.at(0);
v.front();
v.back();
// Size operations
v.size();
v.capacity(); // Allocated space
v.resize(20); // Change size
v.reserve(100); // Pre-allocate
// Remove
v.pop_back(); // Remove last
v.clear(); // Remove all
v.erase(v.begin()); // Remove first
Arrays Comparison
| Feature | C-Array | std::array | std::vector |
|---|---|---|---|
| Size | Fixed, compile-time | Fixed, compile-time | Dynamic |
| Bounds Check | No | Yes (.at()) | Yes (.at()) |
| Memory | Stack | Stack | Heap |
| Knows Size | No (decays) | Yes | Yes |
| Can Resize | No | No | Yes |
| STL Compatible | Limited | Full | Full |
| When to Use | C compatibility | Fixed size, stack | Dynamic needs |
Practice Questions
- Write a function to find the maximum element in an array.
- Implement a function to reverse an array in place.
- Create a 2D array representing a tic-tac-toe board and display it.
- Write a program to transpose a matrix (swap rows and columns).
- Implement a function to count vowels in a string.
- Write a palindrome checker for strings.
- Create a function to split a string by a delimiter.
- Implement a simple word count program.
- Write a program to remove duplicate characters from a string.
- Convert a sentence to title case (first letter of each word uppercase).
- Implement string compression ("aabbbcc" → "a2b3c2").
- Create a program to find all anagrams of a word from a list.