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 NULL if needed)

Type* ptr = NULL;

Changing a Variable Through a Pointer

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

Complete Program

#include <stdio.h>

int main() {
    int a = 5;
    int* a_ptr = &a;  // &: address-of operator
    *a_ptr += 5;  // *: dereference operator
    printf("updated value: %d (at %p - %zu bytes)\n", a, a_ptr, sizeof(a_ptr));
    return 0;
}

Visualize execution

Pointer to a Pointer

#include <stdio.h>

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** p = &a_ptr;
  *p = NULL;  // set a_ptr to invalid location
  *p = b_ptr;
  **p *= **p;  // use multiple lines to make readable
  printf("a = %d, b = %d\n", a, b);
  return 0;
}

Visualize execution

Examples

Update Function Parameters

Avoid changing parameters whenever possible!

#include <stdio.h>

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);
  printf("updated values: %d %d\n", a, b);
  return 0;
}

Visualize execution

Iterate Over an Array

#include <stdio.h>

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

int main() {
  int array[] = {1, 2, 3, 4, 5, 6};
  int value = sum(array, sizeof(array) / sizeof(int));
  printf("sum: %d\n", value);
  return 0;
}

Visualize execution

String Operations

#include <stdio.h>
#include <ctype.h>

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);
  printf("%s\n", name);
  return 0;
}

Visualize execution

Pointer to Struct

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

#include <stdio.h>
typedef struct {int year; int month; int day;} IsoDate;

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

Questions
and feedback...