C Pointers 101: Part 1 – A Gentle Introduction

If the C language’s pointer feature has ever intimidated you, you’re not alone. Many programmers new to the language have been tripped up by the concept of pointers, but I believe it’s because pointers are poorly taught.

In this series, we’re going to learn about pointers at a slow pace, just enough to not let them intimidate us anymore. This is part 1, a gentle introduction. Part 2 will be about passing by reference, and part 3 will touch on the ever daunting “pointer arithmetic.” Ready to dive in? I sure am.

What’s a pointer?

In C, a pointer “points to” the address of a section of memory that holds a value. What does that mean? Well, for right now, it doesn’t matter. Let’s start with examples first, then we’ll dive in a little deeper.

If your environment is set up to write and compile C, go ahead and fire it up, otherwise just follow along, I’ll share the output of the code samples.

My Friends and their Addresses

I have a friend, his name is “one.” He lives in a house inside my computer program. Let’s find out what his address is.

#include <stdio.h>
int main() {
  int friend_one = 1;
  printf("My friend %i lives at address %p \n", friend_one, &friend_one);

  return 0;
}

Let’s compile and run and see what happens:

$ gcc -o pointersPart1 pointersPart1.c
$ ./pointersPart1
My friend 1 lives at address 0x7ffc52e56554

If you’re following along, it’s almost guaranteed that your address will be different than mine, don’t worry about that, it’s normal.

What was that?

For right now, let’s stick with the address analogy.

Our friend int friend_one is needed to run this program, so the computer has allocated some land for him. The address of that land is 0x7ffc52e56554 (technically, the address “starts” there, we’ll cover that later).

Whenever the program needs our buddy friend_one, the computer knows exactly where to go find him, at that address. And now, you know where to look for him too, because the ampersand (&) is how you get a variable’s address.

Let’s look at another example:

#include <stdio.h>
int main() {
  int friend_one = 1;
  int friend_two = 2;

  int *address_one = &friend_one;
  int *address_two = &friend_two;

  printf("My friend_one(%i) lives at %p\n", friend_one, address_one);
  printf("My friend_two(%i) lives at %p\n", friend_two, address_two);

  printf("Who lives at %p? %i does\n", address_one, *address_one);
  printf("Who lives at %p? %i does\n", address_two, *address_two);

  return 0;
}

Compile and run:

$ gcc -o pointersPart1_2 pointersPart1_2.c
$ ./pointersPart1_2
My friend_one(1) lives at 0x7ffc763f2440
My friend_two(2) lives at 0x7ffc763f2444
Who lives at 0x7ffc763f2440? 1 does
Who lives at 0x7ffc763f2444? 2 does

Let’s ditch the analogy and talk about what’s really going on here. We declare 2 integer variables, friend_one and friend_two and assign them the value 1 and 2 respectively. Now, the next part int *address_one is how you declare a pointer for an integer variable. Then we set the value of this pointer to &friend_one.

So now, the pointer variables are pointing to those addresses. Then, we want to actually see that value. In C, the string interpolation operator for pointers is %p, and when we put address_one in that printf function, we get the actual address.

The real magic here, though, is the last two lines:

printf("Who lives at %p? %i does\n", address_one, *address_one);
printf("Who lives at %p? %i does\n", address_two, *address_two);

We know the pointer is pointing to the address, but what if we want the pointer to tell us what’s in the address? Then we use the *pointer_variable_name to get that information.

A bit of theory before you go

Real quick, let’s talk about memory allocation and the stack vs the heap. When you program fires up, the computer allocates a section of memory for the program to run. The memory allocated to the program is the program’s stack, and the remaining memory on the computer is called the heap.

Each one of those addresses represents one byte, so if your computer has 4GB of RAM, it has 4 x 1,073,741,824 unique addresses to work with. An integer variable typically takes up 4 bytes, which is why the two variable addresses up there are 4 apart. Remember when I said the integer’s address actually started at 0x7ffc52e56554? That’s because the integer is taking up
0x7ffc52e56554
0x7ffc52e56555
0x7ffc52e56556
and 0x7ffc52e56557

This is why most compiled languages are statically typed (the ones where you declare variable data types). The compiler (or its equivalent) must know how many addresses the program will take up so the computer can allocate accordingly.

In C and other languages that allow direct memory manipulation, your program is not confined to the memory it’s allocated, it can borrow against heap memory. In C, malloc() is used to borrow heap memory and free() gives it back to the computer. Likewise, in C++, the new and delete keywords operate similarly.

If you ever hear about “garbage collection” (or gc for the cool kids) in programming languages, that refers to a language’s ability to free the memory its programs use back to the computer once the memory is out of use. In languages like C and C++ where you are in control of deallocating the memory you borrow, gc is up to you. Sometimes there will be bugs in which memory is not released back to the computer once it’s no longer needed, this is what a “memory leak” is.

Anyway, I’ve bored you enough. Check out the next article for some stuff we can actually do with pointers.


Posted

in

by