System Programming Topic
C++ Arrays, Strings & Pointers
1. Arrays
One-dimensional Arrays
cpp
// Declaration and initialization
int numbers[5]; // Declaration only (contains garbage)
int scores[5] = {85, 90, 78, 92, 88}; // Declaration with initialization
int marks[] = {95, 87, 92}; // Size automatically calculated (3)
int zeros[10] = {0}; // All elements initialized to 0
int arr[5] = {1, 2}; // First two: 1,2; rest: 0
// Accessing elements
numbers[0] = 10; // First element (index 0)
numbers[4] = 50; // Last element (size-1)
int x = numbers[2]; // Read value
// Array size
int size = sizeof(numbers) / sizeof(numbers[0]); // Get array size
Multi-dimensional Arrays
cpp
// 2D Array (Matrix)
int matrix[3][3]; // 3 rows, 3 columns
// Initialization
int grid[2][3] = {
{1, 2, 3}, // Row 0
{4, 5, 6} // Row 1
};
// Accessing elements
grid[0][1] = 10; // Row 0, Column 1
int value = grid[1][2]; // Row 1, Column 2 = 6
// 3D Array
int cube[2][3][4] = {
{ // Layer 0
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
},
{ // Layer 1
{13,14,15,16},
{17,18,19,20},
{21,22,23,24}
}
};
// Looping through arrays
int arr[5] = {1,2,3,4,5};
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
// 2D array loop
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 3; j++) {
cout << grid[i][j] << " ";
}
cout << endl;
}
Array Initialization Methods
cpp
// Different initialization methods
int arr1[5]; // Garbage values
int arr2[5] = {}; // All zeros
int arr3[5] = {0}; // All zeros
int arr4[5] = {1,2,3}; // {1,2,3,0,0}
int arr5[] = {1,2,3,4,5}; // Size = 5
// Designated initializers (C++20)
int arr6[5] = {[0] = 1, [2] = 3, [4] = 5}; // {1,0,3,0,5}
Passing Arrays to Functions
cpp
#include <iostream>
using namespace std;
// Method 1: As pointer (most common)
void printArray(int* arr, int size) {
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// Method 2: As array with size
void printArray2(int arr[], int size) {
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// Method 3: Fixed-size array (size must match exactly)
void printFixedArray(int (&arr)[5]) {
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// Method 4: Using template for any size
template<size_t N>
void printTemplateArray(int (&arr)[N]) {
for(size_t i = 0; i < N; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// Passing 2D array
void print2DArray(int arr[][3], int rows) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 3; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}
int main() {
int numbers[] = {1,2,3,4,5};
int matrix[2][3] = {{1,2,3},{4,5,6}};
printArray(numbers, 5);
printArray2(numbers, 5);
printFixedArray(numbers);
printTemplateArray(numbers);
print2DArray(matrix, 2);
return 0;
}
Array Limitations & Modern Alternatives
cpp
// C++ array limitations:
// 1. Fixed size
// 2. No bounds checking
// 3. Passed as pointer (loses size information)
// Modern alternatives:
#include <array> // std::array (fixed size)
#include <vector> // std::vector (dynamic size)
// std::array (C++11)
#include <array>
std::array<int, 5> arr = {1,2,3,4,5};
cout << "Size: " << arr.size() << endl;
cout << "Front: " << arr.front() << endl;
cout << "Back: " << arr.back() << endl;
arr.fill(0); // Fill all with 0
// std::vector (dynamic array)
#include <vector>
std::vector<int> vec = {1,2,3};
vec.push_back(4); // Add element
vec.pop_back(); // Remove last
2. Strings
C-style Strings (Character Arrays)
cpp
// C-style strings are null-terminated character arrays
char str1[20]; // Uninitialized
char str2[20] = "Hello"; // Size 20, content "Hello\0"
char str3[] = "World"; // Size 6 (5 chars + null terminator)
char str4[10] = {'H', 'i', '\0'}; // Manual null termination
// Important: Always include space for null terminator '\0'
char name[6] = "Alice"; // 'A','l','i','c','e','\0'
// String functions from <cstring>
#include <cstring>
char str1[20] = "Hello";
char str2[20] = "World";
strlen(str1); // Length: 5
strcpy(str1, str2); // Copy str2 to str1
strncpy(str1, str2, 3); // Copy first 3 chars
strcat(str1, str2); // Concatenate
strcmp(str1, str2); // Compare: 0=equal, <0=str1<str2, >0=str1>str2
strchr(str1, 'l'); // Find first occurrence of 'l'
strstr(str1, "lo"); // Find substring
// Example usage
char source[] = "Hello World";
char destination[50];
strcpy(destination, source);
cout << destination << endl; // Hello World
strcat(destination, " C++");
cout << destination << endl; // Hello World C++
// Compare strings
if(strcmp(source, "Hello World") == 0) {
cout << "Strings are equal" << endl;
}
// Find character
char* pos = strchr(source, 'W');
if(pos) {
cout << "Found at position: " << (pos - source) << endl;
}
std::string (Modern C++)
cpp
#include <iostream>
#include <string>
using namespace std;
// Declaration and initialization
string s1; // Empty string
string s2 = "Hello"; // From C-string
string s3("World"); // Constructor
string s4 = s2; // Copy
string s5(5, 'A'); // "AAAAA"
string s6 = {'H', 'i'}; // Initializer list
// Input/Output
string name;
cout << "Enter name: ";
cin >> name; // Single word
getline(cin, name); // Whole line (with spaces)
// Common operations
string str = "Hello World";
// Access
cout << str[0]; // H
cout << str.at(1); // e (with bounds checking)
// Size
cout << str.length(); // 11
cout << str.size(); // 11
cout << str.empty(); // false
// Modification
str.append(" C++"); // "Hello World C++"
str.push_back('!'); // Add single character
str.insert(5, " beautiful"); // "Hello beautiful World"
str.erase(5, 10); // Remove 10 chars from position 5
str.replace(6, 5, "C++"); // Replace 5 chars from position 6
str.clear(); // Make empty
// Concatenation
string s1 = "Hello";
string s2 = "World";
string s3 = s1 + " " + s2; // "Hello World"
s1 += " C++"; // Append
// Comparison
if(s1 == s2) cout << "Equal";
if(s1 != s2) cout << "Not equal";
if(s1 < s2) cout << "s1 < s2"; // Lexicographical
// Substring
string sub = str.substr(6, 5); // From position 6, 5 chars = "World"
size_t pos = str.find("World"); // Find substring
size_t rpos = str.rfind("l"); // Find last occurrence
// Conversion
string numStr = to_string(123); // "123"
int num = stoi("456"); // 456
double d = stod("3.14159"); // 3.14159
// Iteration
for(char c : str) {
cout << c << " ";
}
for(auto it = str.begin(); it != str.end(); ++it) {
cout << *it << " ";
}
// C-string conversion
const char* cstr = str.c_str(); // Read-only C-string
char buffer[50];
str.copy(buffer, 5, 0); // Copy 5 chars to buffer
// String streams
#include <sstream>
stringstream ss;
ss << "Age: " << 25 << " Name: " << "John";
string info = ss.str();
String Functions & Methods
cpp
string text = "Hello World Programming";
// Searching
size_t found = text.find("World"); // 6
found = text.find('o'); // 4 (first)
found = text.find('o', 5); // 7 (from position 5)
found = text.find_first_of("aeiou"); // 1 (first vowel)
found = text.find_last_of("aeiou"); // 21 (last vowel)
found = text.find_first_not_of("Helo "); // 5 (W)
// Replacement
text.replace(6, 5, "C++"); // "Hello C++ Programming"
// Substring extraction
string word = text.substr(6, 5); // "World"
string suffix = text.substr(6); // "World Programming"
// Case conversion
#include <algorithm>
#include <cctype>
string mixed = "Hello World";
transform(mixed.begin(), mixed.end(), mixed.begin(), ::toupper);
cout << mixed << endl; // HELLO WORLD
transform(mixed.begin(), mixed.end(), mixed.begin(), ::tolower);
cout << mixed << endl; // hello world
// Trimming whitespace
#include <algorithm>
#include <cctype>
string spaced = " Hello World ";
// Trim left
spaced.erase(spaced.begin(), find_if(spaced.begin(), spaced.end(),
[](unsigned char ch) { return !isspace(ch); }));
// Trim right
spaced.erase(find_if(spaced.rbegin(), spaced.rend(),
[](unsigned char ch) { return !isspace(ch); }).base(), spaced.end());
cout << "[" << spaced << "]" << endl; // [Hello World]
// Splitting string
vector<string> splitString(const string& str, char delimiter) {
vector<string> tokens;
string token;
stringstream ss(str);
while(getline(ss, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
string data = "apple,banana,cherry,date";
vector<string> fruits = splitString(data, ',');
// fruits = {"apple", "banana", "cherry", "date"}
3. Pointers
Pointer Basics
cpp
int x = 10; int* ptr = &x; // ptr stores address of x cout << "Value of x: " << x << endl; // 10 cout << "Address of x: " << &x << endl; // 0x... cout << "Value of ptr: " << ptr << endl; // Same address as &x cout << "Value at ptr: " << *ptr << endl; // 10 (dereferencing) // Modifying through pointer *ptr = 20; cout << "x is now: " << x << endl; // 20 // Pointer declaration styles int* p1; // Most common int * p2; // Also valid int *p3; // Also valid int* p4, p5; // p4 is pointer, p5 is int (careful!) // Pointer to different types double d = 3.14; double* dptr = &d; char c = 'A'; char* cptr = &c;
Pointer Arithmetic
cpp
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // Points to first element
cout << *ptr << endl; // 10
ptr++; // Move to next int
cout << *ptr << endl; // 20
ptr += 2; // Move 2 ints forward
cout << *ptr << endl; // 40
ptr--; // Move back
cout << *ptr << endl; // 30
// Pointer differences
int* ptr1 = &arr[0];
int* ptr2 = &arr[3];
cout << "Difference: " << (ptr2 - ptr1) << endl; // 3 elements
// Valid operations
int* p = arr;
p++; // Move to next element
p--; // Move to previous element
p = p + 3;// Move 3 elements forward
p = p - 2;// Move 2 elements back
// Invalid operations
// p = p * 2; // Error
// p = p / 2; // Error
// p = p % 2; // Error
Pointers & Arrays
cpp
int numbers[5] = {1, 2, 3, 4, 5};
int* ptr = numbers; // ptr points to first element
// Array name is a pointer to first element
cout << numbers << endl; // Address of first element
cout << &numbers[0] << endl; // Same address
// Accessing array elements via pointer
cout << *(numbers + 0) << endl; // 1
cout << *(numbers + 1) << endl; // 2
cout << *(numbers + 2) << endl; // 3
// Pointer iteration through array
for(int* p = numbers; p < numbers + 5; p++) {
cout << *p << " ";
}
// Array of pointers
int a = 10, b = 20, c = 30;
int* ptrArr[3] = {&a, &b, &c};
for(int i = 0; i < 3; i++) {
cout << *ptrArr[i] << " "; // 10 20 30
}
// Pointer to array
int (*arrayPtr)[5] = &numbers; // Pointer to entire array
cout << (*arrayPtr)[2] << endl; // 3
// Dynamic array with pointer
int size = 5;
int* dynamicArray = new int[size]; // Heap allocation
for(int i = 0; i < size; i++) {
dynamicArray[i] = i * 10;
}
delete[] dynamicArray; // Don't forget to free!
Pointers to Functions
cpp
#include <iostream>
using namespace std;
// Function prototypes
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
// Function pointer type
typedef int (*Operation)(int, int); // Old style
using Operation2 = int(*)(int, int); // Modern (C++11)
int main() {
// Function pointer declaration
int (*funcPtr)(int, int);
// Assign function to pointer
funcPtr = add;
cout << funcPtr(5, 3) << endl; // 8
funcPtr = subtract;
cout << funcPtr(5, 3) << endl; // 2
// Array of function pointers
Operation operations[] = {add, subtract, multiply};
for(int i = 0; i < 3; i++) {
cout << operations[i](10, 5) << endl;
// 15, 5, 50
}
// Using with switch
char op = '+';
int x = 10, y = 5;
switch(op) {
case '+': funcPtr = add; break;
case '-': funcPtr = subtract; break;
case '*': funcPtr = multiply; break;
}
cout << "Result: " << funcPtr(x, y) << endl;
return 0;
}
// Callback function example
#include <vector>
#include <algorithm>
void printSquare(int n) {
cout << n * n << " ";
}
void processNumbers(vector<int> nums, void (*callback)(int)) {
for(int num : nums) {
callback(num);
}
}
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
processNumbers(numbers, printSquare); // 1 4 9 16 25
return 0;
}
Null Pointers
cpp
// Old style (avoid in C++)
int* ptr1 = NULL; // Defined as 0
int* ptr2 = 0; // Literal 0
// Modern C++ (preferred)
int* ptr3 = nullptr; // C++11
// Checking for null
int* ptr = nullptr;
if(ptr == nullptr) {
cout << "Pointer is null" << endl;
}
if(!ptr) { // Shorthand
cout << "Pointer is null" << endl;
}
// Dereferencing null pointer causes undefined behavior
// DON'T DO THIS:
// int* nullPtr = nullptr;
// *nullPtr = 10; // CRASH!
// Use case: Optional return
int* findValue(int arr[], int size, int target) {
for(int i = 0; i < size; i++) {
if(arr[i] == target) {
return &arr[i]; // Return address if found
}
}
return nullptr; // Return null if not found
}
// Smart pointers (C++11) - better alternative
#include <memory>
std::unique_ptr<int> smartPtr = std::make_unique<int>(42);
// Automatically manages memory
Void Pointers
cpp
// void* can point to any data type
int num = 10;
float f = 3.14;
char c = 'A';
void* vptr;
vptr = # // Point to int
cout << *(static_cast<int*>(vptr)) << endl; // Must cast to use
vptr = &f; // Point to float
cout << *(static_cast<float*>(vptr)) << endl;
vptr = &c; // Point to char
cout << *(static_cast<char*>(vptr)) << endl;
// Use case: Generic functions
void printValue(void* ptr, char type) {
switch(type) {
case 'i':
cout << *(static_cast<int*>(ptr)) << endl;
break;
case 'f':
cout << *(static_cast<float*>(ptr)) << endl;
break;
case 'c':
cout << *(static_cast<char*>(ptr)) << endl;
break;
}
}
int main() {
int x = 42;
float y = 3.14;
char z = 'X';
printValue(&x, 'i'); // 42
printValue(&y, 'f'); // 3.14
printValue(&z, 'c'); // X
return 0;
}
4. References
Reference Variables
cpp
int x = 10; int& ref = x; // ref is a reference to x (alias) cout << "x = " << x << endl; // 10 cout << "ref = " << ref << endl; // 10 ref = 20; // Modify through reference cout << "x = " << x << endl; // 20 (x also changed!) // Must be initialized when declared int& ref2; // ERROR: must be initialized // References are like constant pointers int y = 30; // ref = y; // This reassigns value, not reference itself // After this, ref still refers to x, x becomes 30 // References to different types double d = 3.14; double& dref = d; string s = "Hello"; string& sref = s;
Pass by Reference
cpp
#include <iostream>
using namespace std;
// Pass by value (copy)
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
// Changes only affect local copies
}
// Pass by reference
void swapByReference(int& a, int& b) {
int temp = a;
a = b;
b = temp;
// Changes affect original variables
}
// Pass by pointer
void swapByPointer(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Return by reference (careful with scope!)
int& getElement(int arr[], int index) {
return arr[index]; // Return reference to array element
}
// Const reference (read-only, efficient for large objects)
void printLargeObject(const vector<int>& vec) {
// vec cannot be modified here
for(int num : vec) {
cout << num << " ";
}
}
int main() {
int x = 5, y = 10;
swapByValue(x, y);
cout << "x=" << x << ", y=" << y << endl; // Still 5, 10
swapByReference(x, y);
cout << "x=" << x << ", y=" << y << endl; // 10, 5
swapByPointer(&x, &y);
cout << "x=" << x << ", y=" << y << endl; // 5, 10
// Modify array element via reference return
int arr[] = {1, 2, 3, 4, 5};
getElement(arr, 2) = 100; // arr[2] = 100
cout << arr[2] << endl; // 100
return 0;
}
References vs Pointers
cpp
// Comparison table:
// | Feature | Reference | Pointer |
// |----------------|-------------------------|-------------------------|
// | Declaration | int& ref = var; | int* ptr = &var; |
// | Reassignment | Cannot be reassigned | Can be reassigned |
// | Null value | Cannot be null | Can be null |
// | Indirection | Automatic (ref) | Manual (*ptr) |
// | Address-of | &ref gives var's address| &ptr gives ptr's address|
// | Arithmetic | Not allowed | Allowed |
// | Memory | No separate memory | Has its own memory |
// Examples:
int value = 42;
// Reference
int& ref = value; // Must be initialized
// int& ref2; // ERROR: must initialize
// ref = NULL; // ERROR: cannot be null
// &ref is same as &value
// Pointer
int* ptr = &value; // Can be initialized later
ptr = nullptr; // Can be null
int* ptr2; // OK (uninitialized, dangerous!)
ptr2 = &value; // Reassignment allowed
ptr++; // Pointer arithmetic allowed
// Function parameters
void funcRef(int& param) {
param = 100; // Direct modification
}
void funcPtr(int* param) {
if(param) { // Must check for null
*param = 100; // Dereference needed
}
}
int main() {
int x = 10;
funcRef(x); // Call with variable
cout << x << endl; // 100
funcPtr(&x); // Call with address
cout << x << endl; // 100
// When to use each:
// Use references when:
// 1. Function needs to modify argument
// 2. Avoid copying large objects
// 3. Operator overloading
// 4. Range-based for loops
// Use pointers when:
// 1. Need to handle optional/null values
// 2. Need pointer arithmetic
// 3. Dynamic memory allocation
// 4. Working with C APIs
// 5. Polymorphism with base class pointers
return 0;
}
// Reference in range-based for loop
vector<int> numbers = {1, 2, 3, 4, 5};
for(int& num : numbers) { // Reference avoids copy
num *= 2; // Modify original elements
}
// Pointer for polymorphism
class Base { virtual void show() {} };
class Derived : public Base {};
Base* bptr = new Derived(); // Base class pointer to derived object
// Use dynamic_cast for safe downcasting
Reference to Pointer and Pointer to Reference
cpp
// Reference to pointer
int x = 10;
int y = 20;
int* ptr = &x;
int*& refToPtr = ptr; // Reference to a pointer
refToPtr = &y; // Now ptr points to y
cout << *ptr << endl; // 20
// Pointer to reference is NOT allowed
// int&* ptrToRef; // ERROR: pointer to reference is illegal
// Pointer to pointer (double pointer)
int value = 42;
int* ptr1 = &value;
int** ptr2 = &ptr1; // Pointer to pointer
cout << **ptr2 << endl; // 42 (dereference twice)
// Use case: Modify pointer in function
void allocateMemory(int** ptr) {
*ptr = new int(100); // Modify the original pointer
}
int main() {
int* myPtr = nullptr;
allocateMemory(&myPtr);
cout << *myPtr << endl; // 100
delete myPtr;
return 0;
}
Best Practices Summary
cpp
// 1. Prefer references over pointers when:
// - No null value needed
// - No reassignment needed
// - Working within current scope
// 2. Use pointers when:
// - Dealing with dynamic memory
// - Need optional/null values
// - Working with C interfaces
// - Implementing data structures
// 3. Use const references for:
// - Large object parameters
// - Read-only access
// 4. Avoid raw pointers for ownership:
// - Use smart pointers (unique_ptr, shared_ptr) instead
// 5. Always initialize pointers:
// - Either to valid address or nullptr
// 6. Check for null before dereferencing
// 7. Use references in range-based for loops
// 8. Be careful with reference returns:
// - Never return reference to local variable
// Example of good practices:
#include <memory>
#include <vector>
void processData(const std::vector<int>& data) { // Const reference
for(const int& num : data) { // Reference in loop
// Process without copying
}
}
std::unique_ptr<int[]> createArray(int size) { // Smart pointer
return std::make_unique<int[]>(size); // Automatic cleanup
}
int& getFromArray(std::vector<int>& arr, int index) {
if(index >= 0 && index < arr.size()) {
return arr[index]; // OK: returns reference to existing element
}
throw std::out_of_range("Invalid index");
}
// BAD: Returning reference to local
int& badFunction() {
int local = 42;
return local; // DANGER: local destroyed after function returns
}
Complete Example: String Manipulation Program
cpp
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <memory>
using namespace std;
// Function using C-style strings
void processCString(const char* str) {
char buffer[100];
strcpy(buffer, str);
cout << "C-string length: " << strlen(buffer) << endl;
cout << "Uppercase: ";
for(int i = 0; buffer[i] != '\0'; i++) {
buffer[i] = toupper(buffer[i]);
cout << buffer[i];
}
cout << endl;
}
// Function using std::string
void processStdString(const string& str) {
string upper = str;
for(char& c : upper) {
c = toupper(c);
}
cout << "std::string length: " << str.length() << endl;
cout << "Uppercase: " << upper << endl;
// Find vowels using pointer-like iterators
cout << "Vowels at positions: ";
for(auto it = str.begin(); it != str.end(); ++it) {
char c = tolower(*it);
if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
cout << (it - str.begin()) << " ";
}
}
cout << endl;
}
// Function demonstrating pointers and references
void modifyValues(int* ptr, int& ref) {
if(ptr) {
*ptr *= 2; // Modify through pointer
}
ref += 10; // Modify through reference
}
int main() {
// C-style string
char cstr[] = "Hello World";
processCString(cstr);
// std::string
string str = "Programming in C++";
processStdString(str);
// Pointers and references
int a = 5, b = 10;
int* ptr = &a;
int& ref = b;
cout << "\nBefore: a=" << a << ", b=" << b << endl;
modifyValues(ptr, ref);
cout << "After: a=" << a << ", b=" << b << endl;
// Dynamic array with pointer
int size = 5;
unique_ptr<int[]> arr(new int[size]); // Smart pointer
// Fill array using pointer arithmetic
for(int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// Access using pointer
int* p = arr.get();
cout << "\nArray elements: ";
for(int i = 0; i < size; i++) {
cout << *(p + i) << " "; // Pointer arithmetic
}
cout << endl;
// No need to delete - smart pointer handles it
return 0;
}
This comprehensive guide covers arrays, strings, pointers, and references in C++ with practical examples. Understanding these concepts is crucial for effective C++ programming and memory management.
Comments (0)
No comments yet. Be the first to comment!