# Introduction to C Pointers

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

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2020-02-09 | Updated: 2026-07-18 | Topics: [C](https://flaviocopes.com/tags/clang/) | Canonical: https://flaviocopes.com/c-pointers/

Pointers are one of the most confusing/challenging parts of [C](https://flaviocopes.com/c-introduction/), in my opinion. Especially if you are new to programming, but also if you come from a higher level programming language like [Python](https://flaviocopes.com/python-introduction/) or [JavaScript](https://flaviocopes.com/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:

```c
int age = 37;
```

we can use the `&` operator to get its address:

```c
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:

```c
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:

```c
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:

```c
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:

```c
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:

```c
#include <stddef.h>

int *address = NULL;
```

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

```c
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](https://flaviocopes.com/c-arrays/) are one example. When you declare an array:

```c
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:

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

We can access the second element with pointer arithmetic:

```c
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.
