Lecture 14 Pointers

Joseph Haugh

University of New Mexico

Read Kernighan and Ritchie Chapter 6

Call by Value Parameters

All function parameters in C are passed call by value.

This means the function gets its own copy of each parameter

Any changes to the parameter values are not seen outside the function

swap Version 1

void swap(int x, int y) {
  int temp;
  temp = x;
  x = y;
  y = temp;
}

void main(void) {
  int a = 10, b = 17;
  swap(a, b);
  printf("a=%d, b=%d\n", a, b);
}

Prints: a=10, b=17

Pointers

A pointer is a variable that contains the address of another variable

You create a pointer using a * in front of the variable name

int *ptr;

ptr is a pointer variable that contains the address of a int variable

Pointers: Null Pointer

What will happen when I run the following code:

void main(void) {
  int *ptr;
  printf("ptr=%p\n", *ptr);
}

Segmentation fault!

That is because we tried to dereference a pointer that was not initialized

Dereferencing a Pointer

The * operator is used to dereference a pointer as well

int *ptr = 10;
int x    = *ptr + 5;
printf("x=%d\n", x);

ptr: is a pointer to an int variable

x: is an int variable with the value 15

& Operator

What if we wanted to save a pointer to an existing variable?

We can use & operator to get the address of a variable

int x;
int *ptr;
ptr = &x;
*ptr = 21;
printf("x=%d\n", x); // Prints: x=21

& Operator

What does the following code do?

int x = 10;
*(&x) = 21;
printf("x=%d\n", x); // Prints: x=21

*(&x) gets the address of x and then dereferences the pointer and sets it to a new value

Pointer Example

Pointer Types

The type of a pointer must match the type of the variable it points to

The following code will cause a waring:

short x;
int   *ptr;
ptr = &x;

Pointer Types

If for some reason you need to do something like above you can add an explicit cast:

short x;
int   *ptr;
ptr = (int *) &x;

Your program can still be wrong but you won’t get a warning from the compiler

Pointer Types

Casting pointers is a common operation in C

For example you can save a pointer with not type as follows:

int  x;
void *ptr = (void *) &x;

However, when you need to explicitly cast it to a type:

int      x;
void     *ptr = (void *) &x;
*((int *) ptr) = 10;

void * Pointers

Why is the void * pointer useful?

Well when memory is allocated by the program it has no inherit type and you need to tell it the type

For example the malloc function returns a void * pointer

void * Pointers

extern   void *malloc(unsigned int size);
extern   void free(void *ptr);

int      *ptr;
ptr = (int *) malloc(sizeof(int));
*ptr = 10;
free((void *) ptr);

Swap Revisited

void swap(int *x, int *y) {
  int tmp = *x;
  *x = *y;
  *y = tmp;
} 

void main(void) {
  int a=10, b=17;
  swap(&a, &b);
  printf("a=%d, b=%d\n", a, b);
}

We have now made the parameters pointers. This will now effect the x and y variables in the main function.

Pointer Example

int j, k;
int *ptr;
int main(void)  {
  j = 1; k = 2;
  ptr = &k;

  printf("j has value %d and is stored at %p\n", 
    j,&j);
  printf("k has value %d and is stored at %p\n", 
    k,&k);
  printf("ptr has value %p and is stored at %p\n",
    ptr,&ptr);
  printf("The value of the int pointed to by ptr is "
    "%d\n", *ptr);
}

Pointer Example

j has value 1 and is stored at 0x404020
k has value 2 and is stored at 0x404024
ptr has value 0x601040 and is stored at 0x404028
The value of the int pointed to by ptr is 2

Pointers

Pointer === Address === Reference === Memory Location

void main(void) {
  int x=6;
  int *y; /* y will be a pointer to an int. */
  y = &x; /* y is assigned the address of x. */
  printf("x=%d, y=%p, *y=%d\n", x, y, *y);
}

Output:

x=6, y=0x7ffcdb19c8d4, *y=6

Overloaded Operators

In C, the * and & operators are overloaded

void main(void) {
  int a = 6;      /* binary: 0110 */
  int b = 3;      /* binary: 0011 */
  int *c = &a;    /* The '&' means address of */
  int x = a * b;  /* The '*' means multiply */
  int y = a + *c; /* The '*' means dereference */
  int z = a & b;  /* The '&' means bitwise AND */
  printf("%d, %d, %d\n", x, y, z);
}

Output:

18, 12, 2

Swap Error: Pass by Value

void swapNot(int x, int y) { 
  printf("swapNot (1) x=%d, y=%d\n", x,y);
  int tmp = x;
  x = y;
  y = tmp;
  printf("swapNot (2) x=%d, y=%d\n", x,y);
}
void main(void) {
  int v[] = {33, 44, 55, 66, 77};
  printf("main (1) v[0]=%d, v[1]=%d\n", 
          v[0],v[1]);
  swapNot(v[0], v[1]); /* Passed by Value */
  printf("main (2) v[0]=%d, v[1]=%d\n", 
         v[0],v[1]);
}
main (1) v[0]=33, v[1]=44
swapNot (1) x=33, y=44
swapNot (2) x=44, y=33
main (2) v[0]=33 v[1]=44

Working Swap: Array Elements

void swapElements(int v[], int i, int k) { 
  int tmp = v[i];
  v[i] = v[k];
  v[k] = tmp;
}
void main(void) {
  int v[] = {33, 44, 55, 66, 77};
  printf("main (1) v[0]=%d, v[1]=%d\n", 
         v[0], v[1]);
  /* passes address of v[0] */
  swapElements(v, 0, 1); 
  printf("main (4) v[0]=%d, v[1]=%d\n", 
         v[0], v[1]);
}
main (1) v[0]=33, v[1]=44
main (4) v[0]=44, v[1]=33

Working Swap: Pointers

void swap (int *x, int *y) {
  /* value at address x. */
  int tmp = *x; 
  /* val @ addr x assn val @ addr y. */
  *x = *y; 
  *y = tmp; 
}
void main(void) {
  int v[] = {33, 44, 55, 66, 77};
  printf("main (1) v[0]=%d, v[1]=%d\n", 
         v[0],v[1]);
  swap(&v[0], &v[1]); //Passed by Reference
  printf("main (3) v[0]=%d, v[1]=%d\n",
         v[0],v[1]);
}
main (1) v[0]=33, v[1]=44
main (3) v[0]=44, v[1]=33

Array Argument is a Pointer

void swapElements (int v[], int i, int k)
/*       same as: (int* v,  int i, int k) */
/*       same as: (int *v,  int i, int k) */
/*       same as: (int*v,   int i, int k) */

Pointer Declaration Style

void main(void) {
  int* a, b; /* Bad style */
  *a = 5;
  b = 7;
  printf("%d, %d\n", *a, b);
}

Output:

5, 7

a is a pointer and b is an int

Pointer Declaration Style

You should instead use one of the following styles:

  • int *a, b;
  • int *a;
    int b;

Pointer Arithmetic

What will this code print:

void main(void) {
  int x = 5;
  int *ptr = &x;
  ptr = ptr + 3;
  printf("&x=%p ptr=%p\n", &x, ptr);
}

Output:

&x=0x7ffd5ff4cd64 ptr=0x7ffd5ff4cd70

Why?

Pointer Arithmetic

Pointer arithmetic is done in terms of the size of the type the pointer points to

ptr = ptr + 3;

Really means:

ptr = ptr + 3 * sizeof(int);
ptr = ptr + 3 * 4;
ptr = ptr + 12;

So why then does it look like the addresses only differ by 6?

&x=0x7ffd5ff4cd64 ptr=0x7ffd5ff4cd70

Hexadecimal!

Summary

  • Pointers are variables that store addresses
  • The * and & operators are overloaded
  • Pointers can be used to pass by reference
  • Pointer arithmetic is done in terms of the size of the type the pointer points to