Creating Pointers in C++
Pointer Fundamentals
Pointers are objects that store the memory address of another object (or `nullptr`). They enable indirect access and are essential for dynamic memory, polymorphism, and interfacing with low-level APIs.
Key characteristics:
- Store memory addresses
- Have their own address and size
- Can be reassigned to point somewhere else
- Can be `nullptr` (points to no object)
- Must be dereferenced to access the pointed-to value (and only if not null)
Pointer Declaration Syntax
Use `*` in the declarator to declare a pointer; use `&` (address-of) to take a variable's address. Place the `*` next to the identifier to avoid confusion.
Be careful when declaring multiple variables in one statement: only the names with `*` are pointers.
// Basic pointer declaration
int value = 42;
int* ptr = &value; // ptr stores address of value
// Pointer to pointer
int** ptrToPtr = &ptr;
// Void pointer (generic, not directly dereferenceable)
void* genericPtr = &value; // must cast to correct type before dereference
// Null pointer initialization
int* nullPtr = nullptr;
// Multiple declarators: only 'p1' and 'p3' are pointers here
int* p1, notAPtr, *p3;
// Prefer one declaration per line to avoid mistakes
int* good = &value; // clear and safe
Pointer Initialization Examples
Pointers can be initialized with the addresses of variables or arrays. When printing a `char*` address, cast to `const void*` to avoid it being treated as a C-string.
#include <iostream>
using namespace std;
int main() {
double d = 3.14159;
double* dPtr = &d;
char c = 'A';
char* cPtr = &c;
cout << "Address stored in dPtr: " << dPtr << '\n';
cout << "Address stored in cPtr: " << static_cast<const void*>(cPtr) << '\n';
// Array name decays to pointer to its first element in most expressions
int arr[3] = {10, 20, 30};
int* arrPtr = arr; // equivalent to &arr[0]
cout << "First: " << *arrPtr << '\n';
cout << "Second: " << *(arrPtr + 1) << '\n';
}
Address stored in dPtr: 0x7ffd4d5a5a48 Address stored in cPtr: 0x7ffd4d5a5a47 First: 10 Second: 20
Dereferencing and Member Access
Use `*ptr` to read/write the value, and `->` to access members through a pointer to an object.
#include <iostream>
#include <string>
using namespace std;
struct User { string name; int score; };
int main() {
int x = 5;
int* px = &x;
*px = 7; // writes to x through the pointer
User u{"Alice", 90};
User* pu = &u;
pu->score += 10; // equivalent to (*pu).score += 10;
cout << x << " " << u.score << '\n';
}
7 100
Pointer Arithmetic (Arrays)
Pointer arithmetic advances by element size. Incrementing an `int*` moves by `sizeof(int)` bytes.
#include <iostream>
using namespace std;
int main() {
int a[4] = {2,4,6,8};
int* p = a; // points to a[0]
++p; // now points to a[1]
cout << *p << '\n'; // 4
p += 2; // now points to a[3]
cout << *p << '\n'; // 8
}
4 8
Null & Dangling Pointers
Always initialize pointers. A dangling pointer points to an object that no longer exists (e.g., after `delete` or the end of a variable's lifetime).
int* p = nullptr; // safe default
// Dangling example (do NOT do this):
int* bad;
{
int local = 42;
bad = &local; // becomes dangling when block ends
}
// *bad; // undefined behavior — 'local' no longer exists
Pointer Size and Architecture
For a given target and ABI, all object pointers typically have the same size (e.g., 4 bytes on many 32-bit platforms, 8 bytes on many 64-bit platforms). Exact sizes are implementation-defined and may differ on embedded systems; function-pointer size can also differ.
#include <iostream>
using namespace std;
int main() {
cout << "Size of int*: " << sizeof(int*) << " bytes\n";
cout << "Size of double*: " << sizeof(double*) << " bytes\n";
cout << "Size of char*: " << sizeof(char*) << " bytes\n";
}
Size of int*: 8 bytes Size of double*: 8 bytes Size of char*: 8 bytes