C Pointers Tutorial

In this C tutorial we learn the dreaded pointer in C. We cover how to use the & reference operator to get a memory address, as well as the * dereferencing operator to get the value of a memory address.

We also cover how to use pointers in arrays and functions.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is a pointer

When a program runs it stores its temporary data, like variables, in a random location in a system’s memory. Once the program quits, the data is removed from memory and it becomes available to the system again.

A pointer is simply a variable that stores the address of another variable.

As an example, think of a gated community. Our friend John Doe has invited us to visit him and gave us the location of the gated community.

So, we know the general location of where he lives. Similarly, we know that temporary data is stored in memory, a general location for it.

We can go to the gated community, but if we want to visit our friend, we need his specific house address.

When we get to the security gate, we have to ask the guard in which house our friend with the name John Doe lives. The guard has stored the house address that correlates to our friend’s name. Similarly, if we want the address of a piece of temporary data, we need to find out where it is and store it.

Pointers allow us to do that, they point us to the address of our data in memory, much like the guard would point us to our friends house within the community.

How to get the address of a variable with the reference of operator ( & )

To get the memory address of a variable, we use the unary reference of ( & ) operator. The reference operator is also known as the address of operator.

For example, if we had a variable called num, we use &num to get the memory address.

Example:
#include <stdio.h>

int main()
{
    int num = 3;

    printf("Variable value: %d\n", num);
    printf("Variable address: %d\n", &num);

    return 0;
}

In the example above, we use the unary & operator immediately in front of the variable name to get its address.

Output:
Variable value: 3
Variable address: 6356748

The num variable is stored at the address 6356748. The variable name num is just the name we give to that location.

The address of the variable may be different on your system.

How to get a value from a memory address with the dereferencing operator ( * )

Once we have the memory address of the variable, we want to get the value stored in that address.

To do this we need to dereference the memory address with the unary dereferencing operator ( * ). The dereference operator is also known as the indirection operator.

Example:
#include <stdio.h>

int main()
{
    int num = 3;

    printf("Variable value: %d\n", num);

    // &num gets the address of num
    printf("Variable address: %d\n", &num);

    // *(&num) gets the value pointed to by &num
    printf("Address value: %d\n", *(&num));

    return 0;
}

In the example above, we dereference the memory address to get the value stored at it with *(&num).

How to declare / initialize a pointer

A pointer variable’s structure is similar to that of a regular variable.

Because a pointer is a variable, it needs to have a valid C data type. We also prefix the pointer name * operator.

The * operator when declaring a pointer is not the dereferencing operator, it’s just a similar notation.

Syntax:
type *name;
Example:
#include <stdio.h>

int main()
{
    int *ptr;

    return 0;
}

In the example above, we declare an integer pointer with the name ptr. At this point our pointer doesn’t point to anything, we need to actually point to something in memory.

We can also initialize a pointer to point to memory when it’s declared.

There are two ways to initialize a pointer variable:

  • Use the reference operator ( & ) to get the memory location of a variable.
  • Directly assign one pointer variable to another pointer variable.
Example:
#include <stdio.h>

int main()
{
    int num = 10;

    // Assign the address of
    // num to the pointer
    int *ptr1 = &num;

    // Assign an existing pointer
    // to another pointer variable
    int *ptr2 = ptr1;

    return 0;
}

If we declare a pointer that doesn’t point to anything, it’s good practice to declare it with a NULL value. A NULL pointer is simply a constant with a value of zero.

Example:
#include <stdio.h>

int main()
{
    int *ptr = NULL;

    printf("%d\n", ptr);

    return 0;
}

In the example above, instead of pointing to nothing, or random bits, the pointer has a safe value of 0.

Advantages of using pointers

Using pointers in C provides us with numerous applications and benefits.

  • Pointers reduce the complexity of a program and increases execution speed.
  • A pointer enables us to return multiple values from a function, or access a variable that’s defined outside of a function.
  • Pointers char array strings saves space in memory.
  • Pointers are efficient in handling data tables.
  • Pointers allow us to access dynamically allocated memory.
  • Pointers allow us to implement data structures like linked lists, trees and graphs.
  • We can access any memory location in a system’s memory with pointers.
  • And more…

Pointers and arrays

The values of an array is stored sequentially in memory, that’s to say, one after the other instead of randomly.

Let’s consider the following example.

Example:
#include <stdio.h>

int main()
{
    int num[] = { 10, 20, 30, 40, 50 };

    for(int i = 0; i < 5; ++i)
    {
        printf("num[%d] = %u\n", i, &num[i]);
    }

    return 0;
}

The example above will print each array element’s memory address.

Output:
num[0] = 6356728
num[1] = 6356732
num[2] = 6356736
num[3] = 6356740
num[4] = 6356744

If we look at the last two numbers of each print, we see that the numbers are in sequence but there’s a difference of 4 bytes between each. That’s because an int (typically) takes up 4 bytes of space in memory.

So, each time we increment an integer array pointer by 1, it will increment the memory address by 4. This means that instead of using the indexer with an array, we can simply use a pointer and increment it.

Example:
#include <stdio.h>

int main()
{
    int num[] = { 10, 20, 30, 40, 50 };
    int *pnum = num;

    for(int i = 0; i < 5; ++i)
    {
        printf("num[%d] = %u\n", i, ++pnum);
    }

    return 0;
}

In the example above, we create a pointer that points to the array. Then, in the printf() statement we increment the pointer by 1, which gives us the same results as the previous example.

Output:
num[0] = 6356728
num[1] = 6356732
num[2] = 6356736
num[3] = 6356740
num[4] = 6356744

Basically, if we want to access an element in the array, we add that number’s position to the pointer.

Example:
#include <stdio.h>

int main()
{
    int num[] = { 10, 20, 30, 40, 50 };
    int *pnum = num;

    printf("num[0] = %u\n", pnum);
    printf("num[1] = %u\n", pnum+1);
    printf("num[2] = %u\n", pnum+2);
    printf("num[3] = %u\n", pnum+3);
    printf("num[4] = %u\n", pnum+4);

    return 0;
}

In the example above, we manually add the number to the pointer and again get the same result as before.

Output:
num[0] = 6356728
num[1] = 6356732
num[2] = 6356736
num[3] = 6356740
num[4] = 6356744

Pointers and functions

C allows us to pass a memory address to a function as an argument. Since a pointer is a variable, we can use a pointer to accept an address in the function parameter list.

To accept an address in a function, we declare a pointer variable in the parameter list.

Syntax:
type name(ptrType *ptrName)
{
	// use *ptrName
}
Example:
#include <stdio.h>

void swapNum(int *num1, int *num2);

int main()
{
    return 0;
}

void swapNum(int *num1, int *num2)
{
    int t = *num1;
    *num1 = *num2;
    *num2 = t;
}

In the example above, we declare two parameters as pointers and use them inside the function as we normally would.

When we call the function above, we will have to use an address of a variable instead of the value for the parameters. This is known as passing a value by reference.

Example:
#include <stdio.h>

void swapNum(int *num1, int *num2);

int main()
{
    int n1 = 3;
    int n2 = 5;

    printf("Before swap:\n");
    printf("n1 = %d\n", n1);
    printf("n2 = %d\n\n", n2);

    swapNum(&n1, &n2);

    printf("After swap:\n");
    printf("n1 = %d\n", n1);
    printf("n2 = %d\n\n", n2);

    return 0;
}

void swapNum(int *num1, int *num2)
{
    int t = *num1;
    *num1 = *num2;
    *num2 = t;
}

In the example above we initialize two numbers and print them to the console. Then, we pass their addresses to the function and print them out again.

You may be wondering now why we would go through all this extra effort to use pointers, instead of just regular variables.

Notice that our swapNum() function is void and doesn’t return a value, but we print the changed values of the variables in the main() function.

By using pointers inside the function, we didn’t need to return a changed value. We accessed the variable directly in memory through its address, and changed it there.

This is one of the great advantages of using pointers.

Summary: Points to remember

  • A pointer is a variable that stores the memory address of another variable.
  • We use the reference of operator ( & ) to get the address of a variable.
  • We get the stored value in a memory address with the dereferencing operator ( * ).
  • To declare or initialize a pointer we prefix the variable name with a * operator.
    • In the declaration or initialization, the * operator is not the dereferencing operator.
  • We can access the value in an array pointer by adding the element’s index position to the pointer.
    • In this case, the compiler will automatically add the bytes for the type of value.
  • We can pass a memory address by reference to a function parameter.
    • The parameter must be declared as a pointer.
    • The argument must be the address of a value, instead of the actual value.