Pointers in C++
What is a Pointer?
Imagine a pointer as a variable that holds the memory address of another variable. Instead of storing a value directly, it stores the location where that value resides in your computer’s memory.
Why Use Pointers?
- Direct Memory Access: Pointers allow you to directly interact with memory locations, which can be useful for tasks like dynamic memory allocation and working with hardware.
- Efficiency: Passing pointers to functions can be more efficient than passing large objects by value, as it avoids copying the entire object.
- Dynamic Data Structures: Pointers are essential for building dynamic data structures like linked lists and trees.
Basic Pointer Concepts
Declaration
int *ptr; // Declares a pointer named 'ptr' that can point to an integer
Initialization
int num = 10;
int *ptr = # // Assigns the address of 'num' to 'ptr'
- The
&
operator gets the address of a variable.
Dereferencing
int value = *ptr; // Accesses the value stored at the address pointed to by 'ptr'
The *
operator (when used with a pointer variable) is the dereference operator. It retrieves the value at the memory location.
Example Code:
#include <iostream>
int main() {
int num = 42;
int *ptr = # // 'ptr' now points to 'num'
std::cout << "Value of num: " << num << std::endl; // Output: 42
std::cout << "Address of num: " << &num << std::endl; // Output: (address of num)
std::cout << "Value of ptr: " << ptr << std::endl; // Output: (same as address of num)
std::cout << "Value pointed to by ptr: " << *ptr << std::endl; // Output: 42
*ptr = 99; // Modify the value at the address pointed to by 'ptr'
std::cout << "New value of num: " << num << std::endl; // Output: 99
return 0;
}
Pointer Arithmetic: You can perform arithmetic operations (addition, subtraction) on pointers. This is useful for traversing arrays.
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 'ptr' points to the first element of the array
std::cout << *(ptr + 2) << std::endl; // Output: 3 (accesses the third element)
Pointers to Pointers: You can have pointers that point to other pointers.
int num = 10;
int *ptr1 = #
int **ptr2 = &ptr1; // 'ptr2' points to 'ptr1', which points to 'num'
std::cout << **ptr2 << std::endl; // Output: 10
Void Pointers void *
is a generic pointer that can point to any data type. It's often used in functions that need to work with different data types.
#include <iostream>
// Function to print the value pointed to by a void pointer
void printValue(void *ptr, char type) {
switch (type) {
case 'i': std::cout << *static_cast<int*>(ptr) << std::endl; break;
case 'c': std::cout << *static_cast<char*>(ptr) << std::endl; break;
case 'f': std::cout << *static_cast<float*>(ptr) << std::endl; break;
default: std::cout << "Invalid type" << std::endl;
}
}
int main() {
int num = 10;
char letter = 'A';
float pi = 3.14f;
void *ptr; // Declare a void pointer
ptr = # // Point to an integer
printValue(ptr, 'i'); // Output: 10
ptr = &letter; // Point to a character
printValue(ptr, 'c'); // Output: A
ptr = π // Point to a float
printValue(ptr, 'f'); // Output: 3.14
return 0;
}
Function Pointers: You can store the address of a function in a function pointer. This allows you to pass functions as arguments to other functions.
#include <iostream>
// Function to add two numbers
int add(int a, int b) {
return a + b;
}
// Function to subtract two numbers
int subtract(int a, int b) {
return a - b;
}
int main() {
// Declare a function pointer that can point to functions
// with the signature: int (int, int)
int (*operation)(int, int);
// Assign the address of the 'add' function to the function pointer
operation = add;
std::cout << "Result of addition: " << operation(5, 3) << std::endl; // Output: 8
// Now, assign the address of the 'subtract' function
operation = subtract;
std::cout << "Result of subtraction: " << operation(5, 3) << std::endl; // Output: 2
return 0;
}
Dynamic Memory Allocation: Pointers are crucial for dynamic memory allocation using new
and delete
. This allows you to allocate memory during runtime.
int *ptr = new int; // Allocate memory for an integer
*ptr = 50;
delete ptr; // Deallocate the memory
Uninitialized Pointers: Always initialize pointers before using them. An uninitialized pointer can point to a random memory location, leading to unexpected behavior or crashes.
Dangling Pointers: A dangling pointer is a pointer that points to memory that has been deallocated. Be careful to avoid dangling pointers, as they can cause errors.
Memory Leaks: When you allocate memory dynamically using new
, make sure to deallocate it using delete
when you're finished with it. Otherwise, you'll have a memory leak.
Value Semantics
In C++, value semantics is a way of handling objects where each object has its own independent value. When you copy an object with value semantics, you get a completely new object with its own distinct data.
Examples of Types with Value Semantics in C++
- Fundamental Types:
int
,char
,float
,double
,bool
- Standard Library Containers:
std::string
,std::vector
(by default) - User-Defined Structs and Classes: (Unless you explicitly define copy behavior that does something different)
#include <string>
#include <iostream>
int main() {
std::string original = "Hello";
std::string copy = original;
copy += ", world!";
std::cout << "Original: " << original << std::endl; // Output: Original: Hello
std::cout << "Copy: " << copy << std::endl; // Output: Copy: Hello, world!
}
Reference Semantics
Reference semantics in C++ provide a way to work with objects indirectly through references or pointers. Instead of creating copies of objects, you create aliases that refer to the original object.
Key Concepts
- References (
&
): A reference is like an alternative name for an existing object. It's essentially a pointer that is automatically dereferenced. - Pointers (
*
): A pointer stores the memory address of an object. You can access and modify the object by dereferencing the pointer. - Shared Data: Multiple references or pointers can point to the same object.
- Changes Propagate: Modifications made through one reference or pointer are visible through all other references/pointers to the same object.
#include <iostream>
int main() {
// Using references
int x = 10;
int& ref = x; // ref is a reference to x
ref = 20;
std::cout << "x: " << x << std::endl; // Output: x: 20
// Using pointers
int y = 30;
int* ptr = &y; // ptr points to y
*ptr = 40;
std::cout << "y: " << y << std::endl; // Output: y: 40
return 0;
}
Happy learning C++ :-)