Skip to content
FLAVIO COPES
flaviocopes.com

Introduction to C Pointers

By

Learn what C pointers store, how to obtain and dereference an address safely, how pointers relate to arrays, and what NULL means.

~~~

Pointers are one of the most confusing/challenging parts of C, in my opinion. Especially if you are new to programming, but also if you come from a higher level programming language like Python or JavaScript.

In this post I want to introduce them in the simplest yet not-dumbed-down way possible.

A pointer is an object whose value represents the address of another object or function.

When you declare an integer number like this:

int age = 37;

we can use the & operator to get its address:

printf("%p\n", (void *)&age);

printf() expects a void * argument for the %p conversion, so the example converts &age to void *. The printed representation is implementation-defined and can look different on every run.

We can assign the address to a variable:

int *address = &age;

int *address declares a pointer to an integer. The type matters: it tells the compiler what kind of object we expect to find at that address.

We can use the unary * operator to read the object the pointer points to. This is called dereferencing the pointer:

int age = 37;
int *address = &age;
printf("%d\n", *address); /* 37 */

The * in a declaration is part of the pointer declarator. In an expression, it means “access the object this pointer points to”.

Dereferencing also lets us change that object:

int age;
int *address = &age;

*address = 37;
printf("%d\n", age); /* 37 */

Invalid pointers

A pointer must point to a valid object before you dereference it. This is wrong:

int *address;
*address = 37; /* undefined behavior */

The uninitialized pointer has an indeterminate value.

Use the null pointer constant NULL when a pointer intentionally points to no object:

#include <stddef.h>

int *address = NULL;

You can compare a pointer with NULL, but dereferencing a null pointer causes undefined behavior:

if (address != NULL) {
  printf("%d\n", *address);
}

A pointer can also become invalid when the object it points to reaches the end of its lifetime. For example, you must not use a pointer to dynamically allocated memory after calling free() on that memory.

Pointers are a great opportunity because they force us to think about memory addresses and how data is organized.

Pointers and arrays

Arrays are one example. When you declare an array:

int prices[3] = { 5, 4, 3 };

prices is an array, not a pointer. In most expressions, however, the array expression is automatically converted to a pointer to its first element:

printf("%d\n", *prices); /* 5 */

We can access the second element with pointer arithmetic:

printf("%d\n", *(prices + 1)); /* 4 */

Adding 1 advances by the size of one int, not by one byte. Pointer arithmetic is defined only within an array object and one position past its end. The one-past pointer can be used in comparisons, but it must not be dereferenced.

Strings in C are usually stored as arrays of char ending with a null character, so pointers are also central to string handling.

Pointers have many more applications, including letting a function modify an object owned by its caller, working with dynamically allocated memory, and referring to functions.

Tagged: C · All topics
~~~

Related posts about clang: