Pointers in C with examples

Table of contents


What is a pointer?

A variable named x,

int x;
occupies some memory. A pointer to x,
int *x_ptr = &x;
is a variable which contains the location of x in memory.

X's location in memory is called its address. The ampersand symbol, &, is the “address-of operator”. It gives the address of x in memory. A pointer is a variable which holds a memory address.

The following program,

#include <stdio.h>

int main ()
{
    /* x is an integer variable. */
    int x = 42;
    /* x_ptr is a pointer to an integer variable. */
    int * x_ptr = & x;
    printf ("x = %d\n", x);
    printf ("x_ptr = %p\n", x_ptr);
    return 0;
}
prints out something like
x = 42
x_ptr = 0xbfbfe87c

(The value of x will be the same, but x_ptr will differ on your computer.) x_ptr is a pointer to x.


Assignment and pointers

Given an integer variable foo and a pointer to foo, foo_ptr, a value can be assigned to foo using only foo_ptr. foo_ptr = 42; changes the address in foo_ptr rather than the value of foo. To access the value at a pointer,

*foo_ptr = 42; 
To read the value,
int bar = *foo_ptr; 

The dereference operator *, accesses the value at an address.

#include <stdio.h>

int main ()
{
    int foo = 42;
    int bar = -1;
    int * foo_ptr;

    foo_ptr = & foo;

    printf ("Get the existing values of foo, bar, foo_ptr, and * foo_ptr:\n");

    printf ("foo = %d\n", foo);
    printf ("bar = %d\n", bar);
    printf ("foo_ptr = %p\n", foo_ptr);
    printf ("* foo_ptr = %d\n", * foo_ptr);

    printf ("Change the value of * foo_ptr:\n");

    * foo_ptr = 99;

    printf ("foo = %d\n", foo);
    printf ("bar = %d\n", bar);
    printf ("foo_ptr = %p\n", foo_ptr);
    printf ("* foo_ptr = %d\n", * foo_ptr);

    printf ("Change the value of foo_ptr to & bar:\n");

    foo_ptr = & bar;

    printf ("foo = %d\n", foo);
    printf ("bar = %d\n", bar);
    printf ("foo_ptr = %p\n", foo_ptr);
    printf ("* foo_ptr = %d\n", * foo_ptr);

    return 0;
}

Download it here.

prints out something like

Get the existing values of foo, bar, foo_ptr, and * foo_ptr:
foo = 42
bar = -1
foo_ptr = 0xbfbfe84c
* foo_ptr = 42
Change the value of * foo_ptr:
foo = 99
bar = -1
foo_ptr = 0xbfbfe84c
* foo_ptr = 99
Change the value of foo_ptr to & bar:
foo = 99
bar = -1
foo_ptr = 0xbfbfe848
* foo_ptr = -1


Arrays

Here's a declaration of a three-integer array:

int array[3] = { 45, 67, 89 };

In C, in most places, the name array becomes a pointer to its first element. Most usages of array are equivalent to if array had been declared as a pointer.

For example, if an array is passed to printf, the array decays to a pointer:

printf ("%p\n", array);
prints the address of array[0].

This is called “decaying”. Decaying is an implicit &; array == &array == &array[0]. In English, these expressions read “array”, “pointer to array”, and “pointer to the first element of array” In C, these mean the same thing.


Pointer arithmetic

To print out the elements of array,

#include <stdio.h>
int main ()
{
    int array[] = { 45, 67, 89 };
    int * array_ptr;
    array_ptr = array;
    printf(" first element: %i\n", *(array_ptr++));
    printf("second element: %i\n", *(array_ptr++));
    printf(" third element: %i\n", *array_ptr);
    return 0;
}

Download it here.

This prints out

 first element: 45
second element: 67
 third element: 89

The ++ operator adds 1 to a variable, the same as variable += 1 Here, the type of the pointer is int. When adding to or subtracting from a pointer, the amount is multiplied by the size of the type of the pointer. In the case of our three increments, each 1 was multiplied by sizeof(int).

On current Intel processors, ints are four bytes. So adding 1 to an int pointer changes it by four bytes.


Indexing

printf ("%i\n", array[0]);

OK… what just happened?

This happened:

45

Well, you probably figured that. But what does this have to do with pointers?

This is another one of those secrets of C. The subscript operator (the [] in array[0]) has nothing to do with arrays.

Oh, sure, that's its most common usage. But remember that, in most contexts, arrays decay to pointers. This is one of them: That's a pointer you passed to that operator, not an array.

As evidence,

#include <stdio.h>

int main ()
{
    int array[] = { 45, 67, 89 };
    int *array_ptr = & array[1];
    printf("%i\n", array_ptr[1]);
    return 0;
}

Download it here.

This prints out

89

Here's a diagram:

The second element of array_ptr is the third element of array.

Array points to the first element of the array; array_ptr is set to &array[1], so it points to the second element of the array. So array_ptr[1] is equivalent to array[2]. Array_ptr starts at the second element of the array, so the second element of array_ptr is the third element of array.

Also, you might notice that because the first element is sizeof(int) bytes wide (being an int), the second element is sizeof(int) bytes forward of the start of the array. You are correct: array[1] is equivalent to *(array + 1). (Remember that the number added to or subtracted from a pointer is multiplied by the size of the pointer's type, so that “1” adds sizeof(int) bytes to the pointer value.)


Structures

A structure is created with the struct keyword.

A struct looks like this:

struct foo {
	size_t size;
	char name[64];
};

Each of those declarations inside the block is called a member. Accessing a member looks like this:

struct foo my_foo;
my_foo.size = sizeof(struct foo);

The expression my_foo.size accesses the member size of my_foo.

For a pointer to a structure, it is possible to use

(*foo_ptr).size = new_size;

or the pointer-to-member operator, ->.

foo_ptr->size = new_size;

With multiple indirection:

(*foo_ptr_ptr)->size = new_size; /* One way */
(**foo_ptr_ptr).size = new_size; /* or another */


Multiple indirection

Consider

int    a =  3;
int   *b = &a;
int  **c = &b;
int ***d = &c;

Here are how the values of these pointers equate to each other:

*d == c;

**d == *c == b

***d == **c == *b == a == 3;

The & operator can be thought of as adding asterisks, and the *, ->, and [] operators as removing asterisks.


Pointers and const

The const keyword is used a bit differently when pointers are involved. These two declarations are equivalent:

const int *ptr_a;
int const *ptr_a;

These two, however, are not equivalent:

int const *ptr_a;
int *const ptr_b;

In the first example, the int (i.e. *ptr_a) is const; you cannot do *ptr_a = 42. In the second example, the pointer itself is const; you can change *ptr_b just fine, but you cannot change (using pointer arithmetic, e.g. ptr_b++) the pointer itself.


Function pointers

It's possible to take the address of a function, too. And, similarly to arrays, functions decay to pointers when their names are used. So if you wanted the address of, say, strcpy, you could say either strcpy or &strcpy.

When you call a function, you use an operator called the function call operator. The function call operator takes a function pointer on its left side.

In this example, we pass dst and src as the arguments on the interior, and strcpy as the function (that is, the function pointer) to be called:

enum { str_length = 18U }; /* Remember the NUL terminator! */
char src[str_length] = "This is a string.", dst[str_length];

/* The function call operator in action (notice the function pointer on the left side). */
strcpy(dst, src); 

There's a special syntax for declaring variables whose type is a function pointer.

/* An ordinary function declaration, for reference */
char *strcpy(char *dst, const char *src); 
/* Pointer to strcpy-like function */
char *(*strcpy_ptr)(char *dst, const char *src);
/* Set the value of "strcpy_ptr" to be "strcpy". */
strcpy_ptr =  strcpy;
/* This works too */
strcpy_ptr = & strcpy; 
/* strcpy_ptr = &amp;strcpy[0]; But not this */

Download it here.

Note the parentheses around *strcpy_ptr in the above declaration. These separate the asterisk indicating return type (char *) from the asterisk indicating the pointer level of the variable (*strcpy_ptr — one level, pointer to function).

Also, just like in a regular function declaration, the parameter names are optional:

char *(*strcpy_ptr_noparams)(char *, const char *) = strcpy_ptr; /* Parameter names removed — still the same type */

The type of the pointer to strcpy is char *(*)(char *, const char *); you may notice that this is the declaration from above, minus the variable name. You would use this in a cast. For example:

strcpy_ptr = (char *(*)(char *dst, const char *src))my_strcpy;

As you might expect, a pointer to a pointer to a function has two asterisks inside of the parentheses:

char *(**strcpy_ptr_ptr)(char *, const char *) = &strcpy_ptr;

It is possible to have an array of function-pointers:

char *(*strcpies[3])(char *, const char *) = { strcpy, strcpy, strcpy };
strcpies[0](dst, src);

This declaration declares a function f with no parameters returning an int, a function fip with no parameter specification returning a pointer to an int, and a pointer pfi to a function with no parameter specification returning an int.

int f(void), *fip(), (*pfi)();

This is equivalent to

int f(void);
int *fip(); /* Function returning int pointer */
int (*pfi)(); /* Pointer to function returning int */

A function pointer can even be the return value of a function:

char *(*get_strcpy_ptr(void))(char *dst, const char *src);
returns a pointer to a function of the form
char *strcpy (char *dst, const char *src);

Typedefs can be used to simplify:

typedef char *(*strcpy_funcptr)(char *, const char *);
strcpy_funcptr strcpy_ptr = strcpy;
strcpy_funcptr get_strcpy_ptr (void);

Strings

There is no string type in C. C strings are just arrays of characters:

char str[] = "I am the Walrus";
This array is 16 bytes in length: 15 characters for "I am the Walrus", plus a NUL (byte value 0) terminator. In other words, str[15] (the last element) is 0. This is how the end of the “string” is signaled.

This idiom is the extent to which C has a string type. But that's all it is: an idiom. Except that it is supported by the aforementioned string literal syntax, and the string library.

The functions in string.h work on pointers.

Here's an implementation of strlen which returns the length of a string, not including the NUL terminator:

size_t strlen (const char *str)
{
	size_t len = 0U;
	while (*(str++))
		++len;
	return len;
}

Download it here.

Here's another possible implementation:

size_t strlen(const char *str)
{
    size_t i;
    for (i = 0U; str[i]; ++i)
        ;
    /* When the loop exits, i is the length of the string */
    return i;
}

Download it here.

This one uses indexing, which uses a pointer.

2010-01-16 http://boredzo.org/pointers/


Copyright 2005–2010 Peter Hosey. This work is licensed under a Creative Commons Attribution 2.5 License. This version is a modification of the original text by Ben Bullock. For comments, questions, and corrections, please email Ben Bullock (benkasminbullock@gmail.com). / Privacy / Disclaimer