Lecture 27 - Pointers I

Published

April 20, 2026

Goals

  • Look at how arrays are passed as arguments to functions
  • Learn the basics of pointers

Passing Arrays

At this point, the only thing that we have passed to our functions are integers, which is a little boring. You can probably imagine that passing longs, chars, floats and doubles would be pretty similar (and you would be right). But, what about more interesting things like arrays and strings?

int f(int a[])
{
  int result;
  result = a[0] + a[1];
  return result;
}

int main(int argc, char *argv[])
{
  int a[2];
  int result;
  a[0] = 0x42;
  a[1] = 0x24;

  result = f(a);

  return result;
}

This gives us

0000000000000000 <f>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   48 89 7d e8             mov    QWORD PTR [rbp-0x18],rdi
   8:   48 8b 45 e8             mov    rax,QWORD PTR [rbp-0x18]
   c:   8b 10                   mov    edx,DWORD PTR [rax]
   e:   48 8b 45 e8             mov    rax,QWORD PTR [rbp-0x18]
  12:   48 83 c0 04             add    rax,0x4
  16:   8b 00                   mov    eax,DWORD PTR [rax]
  18:   01 d0                   add    eax,edx
  1a:   89 45 fc                mov    DWORD PTR [rbp-0x4],eax
  1d:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  20:   5d                      pop    rbp
  21:   c3                      ret

0000000000000022 <main>:
  22:   55                      push   rbp
  23:   48 89 e5                mov    rbp,rsp
  26:   48 83 ec 20             sub    rsp,0x20
  2a:   89 7d ec                mov    DWORD PTR [rbp-0x14],edi
  2d:   48 89 75 e0             mov    QWORD PTR [rbp-0x20],rsi
  31:   c7 45 f4 42 00 00 00    mov    DWORD PTR [rbp-0xc],0x42
  38:   c7 45 f8 24 00 00 00    mov    DWORD PTR [rbp-0x8],0x24
  3f:   48 8d 45 f4             lea    rax,[rbp-0xc]
  43:   48 89 c7                mov    rdi,rax
  46:   e8 00 00 00 00          call   4b <main+0x29>
  4b:   89 45 fc                mov    DWORD PTR [rbp-0x4],eax
  4e:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  51:   c9                      leave
  52:   c3                      ret 
  • On lines 31 and 38, we can see the values being loaded into the array
  • On 3f, we use lea – the “load effective address” instruction. This computes the address using our addressing format and then saves the address itself.
  • The result is stored into register rdi, which is argument 1
  • in f(), the value of the argument is stored at [rbp-0x18] (and then put into rax)
  • on line c, we can see it accessing memory using the address in rax to get the first value
  • on line 12, the address is advanced by 4 and then on the next line we access memory again for the second value

The big lesson here is that we aren’t passing the entire array – we are passing the address of the array.

Passing Arrays

Last time we looked at the mechanics of how arrays are passed and determined that we pass around the address, not the values

What will this print out?


int f(int a[])
{
  a[1] = 0x96;
  printf("during\n");
  for (int i = 0; i <= 2; i++)
  {
    printf("%x\n", a[i]);
  }
}

int main(int argc, char *argv[])
{
  int a[2];
  int result = 0x32;
  a[0] = 0x42;
  a[1] = 0x24;
  printf("before\n");
  for (int i = 0; i <= 2; i++)
  {
    printf("%x\n", a[i]);
  }
  f(a);
  printf("after\n");
  for (int i = 0; i <= 2; i++)
  {
    printf("%x\n", a[i]);
  }
}
$ ./func5
before
42
24
32
during
42
96
32
after
42
96
32

There are two things going on here - we are walking off the end of the array and accessing the next variable (the 0x32s) - we can change the values in the array in the function and they stay changed! (we never had two arrays – so our edits happened to the only one that we had)

Pointers

Pointers are the nature compliment to our “address of” operator (&). A pointer is, in essence, a variable that can hold an address. The concept itself isn’t that hard, but the syntax is…. a little unfortunate.

To create a pointer, we add a star after the type. Note that this means that pointers are typed, this is so the compiler can figure out how big the thing being stored there is (though we do have ways to make this explicit or to typecast the type of an expression).

int x = 42;
int * p = &x; //p has the address of x

If we want to read the value stored at p, we dereference it by putting a star in front of it

int y = *p; // y is now 42

This also works on the left hand side of assignment statements, though here we are assigning into that memory location

*p = 255; // x is now 255

What might we use this for?

Take a moment to see if you can figure out this function


void g(int * a, int * b){
    int c = *a;
    *a = * b;
    *b = c;
}

This is a swap function that swaps the values of two variables. The big implication here is that pointers allow us to pass in values that can be changed! of course this purely perceptual – we actually pass in the addresses here and those we can’t change.

Mechanical level

vocabulary

Skills