Pointers are objects that hold a memory address (typically of another variable) as their value.
int x{ 5 };
std::cout << x << '\n'; // print the value of variable x
std::cout << &x << '\n'; // print the memory address of variable x, hexadecimal values
std::cout << *(&x) << '\n'; // print the value at the memory address of variable x (parentheses not required, but make it easier to read)
int x{ 5 };
int* ptr { &x }; // a pointer initialized with the address of variable x
std::cout << *ptr << '\n'; // use dereference operator to print the value at the address that ptr is holding
*ptr = 6; // The object at the address held by ptr (x) assigned value 6
std::cout << x << '\n'; // 6
int* ptr1, ptr2; // incorrect: ptr1 is a pointer to an int, but ptr2 is just a plain int!
int* ptr3, * ptr4; // correct: ptr3 and ptr4 are both pointers to an int
int x{ 5 };
int y{ 6 };
int* ptr { &x }; // ptr points to x
ptr = &y; // change ptr to point at y
std::cout << *ptr << '\n'; // use dereference operator to print the value at the address that ptr is holding (which is y's address)
int x{ 4 };
std::cout << typeid(&x).name() << '\n'; // print the type of &x : int *
#include <cstddef> // for NULL
float* ptr2; // ptr2 is uninitialized
double* ptr1 { NULL }; // ptr1 is a null pointer
int* ptr3 { nullptr }; // ptr3 is a null pointer
if (ptr3 == nullptr) // explicit test for equivalence
std::cout << "ptr3 is null\n";
if (ptr3) // if ptr3 is not a null pointer
std::cout << *ptr3 << '\n'; // okay to dereference
const int x { 5 };
const int* ptr { &x }; // can't change value but can change what's pointing at
// *ptr = 6; // not allowed: we can't change a const value
ptr = &y;
int x{ 5 };
int* const ptr { &x }; // const after the asterisk means this is a const pointer
// ptr = &y; // error: once initialized, a const pointer cannot be changed.
*ptr = 6; // okay: the value being pointed to is non-const
const int* const ptr { &value }; // a const pointer to a const value
void printByValue(std::string val) // The function parameter is a copy of str
{
std::cout << val << '\n'; // print the value via the copy
}
void printByReference(const std::string& ref) // The function parameter is a reference that binds to str
{
std::cout << ref << '\n'; // print the value via the reference
}
void printByAddress(const std::string* ptr) // The function parameter is a pointer that holds the address of str
{
std::cout << *ptr << '\n'; // print the value via the dereferenced pointer
}
std::string str{ "Hello, world!" };
printByValue(str); // pass str by value, makes a copy of str
printByReference(str); // pass str by reference, does not make a copy of str
printByAddress(&str); // pass str by address, does not make a copy of str
void print(int* ptr)
{
assert(ptr); // fail the program in debug mode if a null pointer is passed (since this should never happen)
if (!ptr) // if ptr is a null pointer, early return back to the caller
return;
// if we reached this point, we can assume ptr is valid
}
#include <iostream>
#include <optional> // for std::optional (C++17)
void printIDNumber(const int* id = nullptr)
{
if (id)
std::cout << "Your ID number is " << *id << ".\n";
else
std::cout << "Your ID number is not known.\n";
}
void printIDNumber(std::optional<const int> id = std::nullopt)
{
if (id)
std::cout << "Your ID number is " << *id << ".\n";
else
std::cout << "Your ID number is not known.\n";
}
std::optional
#include <iostream>
#include <optional> // for std::optional (C++17)
// Our function now optionally returns an int value
std::optional<int> doIntDivision(int x, int y)
{
if (y == 0)
return {}; // or return std::nullopt
return x / y;
}
int main()
{
std::optional<int> result1 { doIntDivision(20, 5) };
if (result1) // if the function returned a value
std::cout << "Result 1: " << *result1 << '\n'; // get the value
else
std::cout << "Result 1: failed\n";
return 0;
}
std::optional
std::optional<int> o1 { 5 }; // initialize with a value
std::optional<int> o2 {}; // initialize with no value
std::optional<int> o3 { std::nullopt }; // initialize with no value
if (o1.has_value()) // call has_value() to check if o1 has a value
if (o2) // use implicit conversion to bool to check if o2 has a value
std::cout << *o1; // dereference to get value stored in o1 (undefined behavior if o1 does not have a value)
std::cout << o2.value(); // call value() to get value stored in o2 (throws std::bad_optional_access exception if o2 does not have a value)
std::cout << o3.value_or(42); // call value_or() to get value stored in o3 (or value `42` if o3 doesn't have a value)
Employee* ptr{ &joe };
std::cout << ptr->id << '\n'; // Better: use -> to select member from pointer to object
// access via operator.
std::cout << (*(*ptr).c).y << '\n'; // ugly!
// access via operator->
std::cout << ptr -> c -> y << '\n'; // much nicer
std::cout << (ptr->paw).claws << '\n';