Programming in C++

Pointers

Gerald Senarclens de Grancy

Purpose of Pointers

Which Problems do Pointers Solve?

  • Pass C-arrays to functions
  • Increase performance when passing large data structures to functions
  • Using pointers can improve performance for repetitive operations (eg. iteration)
  • Function pointers
  • Change parameter values from a function
  • Prerequisite for using memory from the heap (free store)
  • Necessary for implementing high level data structures (linked lists, trees, ...)

Which Problems do Pointers Cause?

  • Prone to bugs
  • Syntax can be hard to read and understand
  • Modifying parameters breaks functional programming paradigm
    (use const Type*)
Many of these problems are solved by C++ and higher level languages

Definition

  • A pointer stores the memory address of a variable
  • Pointers can point to other pointers
  • Obtaining the value stored at a pointer's address:
    * (de-reference operator)
  • Getting the address of a variable:
    & (address-of operator)
  • C pointers

Working with Pointers

Declaration

Type* ptr;

Pointers should always be initialized (use nullptr if needed)

Type* ptr = nullptr;

Changing a Variable Through a Pointer

int a = 5;
int* a_ptr = &a;
*a_ptr += 5;

Complete Program

#include <iostream>

int main() {
    int a = 5;
    int* a_ptr = &a;  // &: address-of operator
    *a_ptr += 5;  // *: dereference operator
    std::cout << "updated value: " << a << " (at " << a_ptr << " - ";
    std::cout << sizeof(a_ptr) << " bytes)" << std::endl;
    return 0;
}

Visualize execution

Pointer to a Pointer

#include <iostream>

int main() {
  int a = 4, b = 5;
  int* a_ptr = &a;  // when starting, it is easiest to have a pre- or suffix
  int* b_ptr = &b;  // for every pointer in your programs
  int** ptr_ptr = &a_ptr;
  *ptr_ptr = nullptr;  // set a_ptr to invalid location
  *ptr_ptr = b_ptr;
  **ptr_ptr *= **ptr_ptr;  // use multiple lines to make readable
  std::cout << "a = " << a << ", b = " << b << std::endl;
  return 0;
}

Visualize execution

Examples

Update Function Parameters

Avoid changing parameters whenever possible!

#include <iostream>

void update(int* a_ptr, int* b_ptr) {
  *a_ptr += 10;  // assign new value to memory address of `a`
  *b_ptr = 100;  // assign new value to `b`
  // still possible to return a value if needed
}

int main() {
  int a=4;
  int b=5;
  int* a_ptr = &a;  // &: address-of operator
  update(a_ptr, &b);
  std::cout << "updated values: " << a << ' ' << b << std::endl;
  return 0;
}

Visualize execution

Iterate Over an Array

#include <iostream>

int sum(const int* array, int count) {
  int result = 0;
  while (count--) {
    result += *array++;
  }
  return result;
}

int main() {
  int array[] = {1, 2, 3, 4, 5, 6};
  int value = sum(array, sizeof(array) / sizeof(int));
  std::cout << "sum: " << value << std::endl;
  return 0;
}

Visualize execution

String Operations

#include <iostream>
#include <cctype>

void upper(char* text) {
  while (*text) {  // string ends with the `\0` character
    *text = toupper(*text);  // return upper case character
    text++;
  }
}

int main() {
  char name[] = "Chris";
  upper(name);
  std::cout << name << std::endl;
  return 0;
}

Visualize execution

Pointer to Struct

The arrow operator -> allows direct access to members when using pointers.

#include <cstdio>
struct IsoDate {int year; int month; int day;};

void print_date(const IsoDate* d) {
  printf("%04d-%02d-%02d\n", (*d).year, d->month, d->day);
}

int main() {
  IsoDate date = {2022, 1, 11};
  print_date(&date);
  return 0;
}

Visualize execution

Reference to Struct

However, in C++ it is recommended to use references in this situation.

#include <iostream>
#include <format>
struct IsoDate {int year; int month; int day;};

void print_date(const IsoDate& d) {
  std::cout << std::format("{:04d}-{:02d}-{:02d}\n", d.year, d.month, d.day);
}

int main() {
  IsoDate date = {2022, 1, 11};
  print_date(date);
  return 0;
}

Visualize execution

Questions
and feedback...