close
Cart icon
User menu icon
User icon
Lightbulb icon
How it works?
FAQ icon
FAQ
Contact icon
Contact
Terms of service icon
Terms of service
Privacy policy icon
Privacy Policy
Zdjęcie główne artykułu.

How to use pointers in C language

Pointers can pose numerous challenges - not only for novice programmers. They are a feature that, on the one hand, provides significant capabilities but, at the same time, demands substantial proficiency. In this article, you will familiarize yourself with the essential techniques for working with pointers in the C language. Let's begin!

Basic techniques

A pointer is a variable that possesses a special power. Such a variable is capable of storing the address of another variable, which is the location where specific information is stored. You can imagine a pointer as a special bookmark in your book. To access your favorite chapter, you don't need to flip through all the pages. It's enough to open the book at the page where the bookmark is placed, and you're ready to go.

To work effectively with pointers, you need to understand the inner workings of two special operators:

& - address operator
* - dereference operator

Take a look at the example of using the & (“address operator”):


#include 


int main() {
    int number = 123;
   
    printf("Value of number is: %d \n", number);
    printf("Address of number is: %p \n", &number);


    return 0;
}


Note, that we used a %p modifier to properly format the address of the variable. Here is the final result of our code:


Value of number is: 123
Address of number is: 0x7fff7ff1be3c


First, we declare a variable named number. Then, we display its content, and finally, we refer to its address using the "&" operator.

Here is another example:


#include 


int main() {
    int number = 123;
    int *ptr; // 1
   
    ptr = &number; // 2
   
    // &
    printf("Address of number is: %p \n", &number);
    printf("Value of number is: %d \n", number);
   
    // *
    printf("Address of number is (ptr): %p \n", ptr);
    printf("Value of number is (*ptr): %d \n", *ptr);
   
    return 0;
}


Take a look at this declaration: int *ptr. It is a pointer (*) named ptr that can point to the address of a variable of type int.

Now, let's examine the next declaration: ptr = &number. It means that the pointer ptr will be pointing to the address of the variable number.

After analyzing the subsequent lines of code, we can conclude that ptr is the same as &number, and *ptr is the same as number. This represents the basic philosophy of using pointers. However, in practice, their usage is not as straightforward as in the above example. So let's move on directly to the next topic.

Pointers and arrays

Pointers are often used in conjunction with arrays. First, let's review the basics of working with arrays in the C language:


#include 


int main() {


    int tab[3] = { 1, 2, 3 };
   
    printf("tab[0] = %d \n", tab[0]);
    printf("tab[1] = %d \n", tab[1]);
    printf("tab[2] = %d \n", tab[2]);


    return 0;
}


We have a three-element array of integers. We can access the elements using an index. You surely remember that the first element of an array is indexed at 0. The result of our program looks like this:


tab[0] = 1
tab[1] = 2
tab[2] = 3


Imagine that the elements in the array are arranged closely together. We can leverage this property and use pointers. We will try to achieve the same effect by skillfully manipulating them.

Take a look at the example:


#include 


int main() {


    int tab[3] = { 1, 2, 3 };
   
    int *ptr = &tab[0];
   
    printf("tab[0] = %d \n", *ptr);
    ptr++;
    printf("tab[1] = %d \n", *ptr);
    ptr++;
    printf("tab[2] = %d \n", *ptr);


    return 0;
}


The effect is identical to the previous example, but the code is slightly different. First, we initialize a pointer to the address of the first element of the array:


  int *ptr = &tab[0];


Next, we attempt to access the value stored at the first element of the array using the * (asterisk) operator:


  printf("tab[0] = %d \n", *ptr);


In the next line, we move our pointer one position to the right. This operation allows us to point to the address of the next element in the array, which is the element at index 1:


  ptr++;


You should already be familiar with the subsequent operations. Take your time to carefully analyze the code and try to grasp the true power of pointers.

Function pointers

Function pointers are an advanced mechanism in the C language that allows storing addresses of functions and dynamically invoking them during program execution.

Before we demonstrate how to declare a function pointer, let's prepare some preliminary code with simple functions responsible for adding and subtracting integers.


#include 


void add(int a, int b) {
   printf("Add: %d \n", a + b);
}


void sub(int a, int b) {
   printf("Sub: %d \n", a - b);
}


int main(void) {
   int a = 4, b = 3;
   
   add(a, b);
   sub(a, b);
   
   return 0;
}


Here is the result:


Add: 7
Sub: 1


This code fragment shouldn't give you much trouble. We have two functions at our disposal: add() and sub(). These functions perform addition and subtraction operations, respectively. In a moment, we will want to use a universal function pointer that can handle both of these functions in one go. Take a look at the following example, which demonstrates the power of pointers:


#include 


void add(int a, int b) {
   printf("Add: %d \n", a + b);
}


void sub(int a, int b) {
   printf("Sub: %d \n", a - b);
}


int main(void) {
   int a = 4, b = 3;
   
   void (* operation) (int, int);
   
   operation = add;
   operation(a, b);
   
   operation = sub;
   operation(a, b);
   
   return 0;
}


At first glance, the code may seem very complex. In reality, only one line is crucial here. Let's analyze the declaration of the function pointer and break it down into its components:


  void (* operation) (int, int);


Here is the description of the operation pointer with the type void (*operation)(int, int):

  • void indicates that the pointer points to a function that does not return any value.
  • *operation is the name of the pointer (don't focus too much on the name; the programmer chooses the pointer's name. In this specific case, we named the pointer "operation").
  • (int, int) signifies that the function, to which the pointer points, takes two arguments of type int.

In summary, void (*operation)(int, int) is a declaration of a function pointer that does not return a value and takes two arguments of type int. We can use such a pointer to store the address of a specific function and later invoke that function using the pointer.

Now that we have declared such a pointer, we search for a function that matches its description. Both the sub() and add() functions fulfill the specified criteria. Finally, we can assign the pointer to the chosen function:


  operation = add;


The name of a function represents its address in memory, so we can assign such a pointer to the function's name. Now, there is nothing stopping us from invoking the function using the pointer.


  operation(a, b);


Calling a function using a pointer is analogous to the standard function call. Note that our pointer can be "reconnected" to another function that matches the declaration of the pointer type at any time.


  operation = sub;


So that's how the basics of using function pointers look like. It's a very interesting mechanism.

Summary

Pointers in the C language are a vast topic. We can use them to work with arrays, functions, and more. In the article, we have only shown a portion of the available possibilities. If you want to master pointers and many other practical techniques in C, enlist for the C Developer Career Path. With this career path, you will learn the C language from the basics to an intermediate-advanced level. The path concludes with an exam that grants you the Specialist Certificate, enabling you to demonstrate your proficiency.