A Basic Guide to Pointers in C Programming

Sirus SalariSirus Salari
8 min read

Introduction

This article provides a comprehensive guide on pointers in the C programming language. It starts by defining pointers and explaining their importance in C programming, such as efficient memory management and low-level memory manipulation. The article then delves into the declaration and initialization of pointers, and how to dereference them to access the value stored at the memory address they point to. It further explores pointer arithmetic for efficient navigation through memory, and the use of pointers in functions to facilitate pass-by-reference behavior. The article concludes by emphasizing the importance of practice and understanding of memory management to effectively use pointers and enhance programming abilities.


What are pointers?

A pointer in C is a specialized type of variable that holds the memory address of another variable. Rather than directly interacting with data, pointers provide programmers with the ability to indirectly access and modify memory locations. This level of indirection is a fundamental concept in C programming that opens the door for a range of powerful and flexible programming techniques.

Pointers are particularly useful in C because they enable efficient memory management and low-level memory manipulation. By utilizing pointers, developers can create dynamic data structures, pass large data sets to functions without duplicating memory, and interact with hardware devices. Furthermore, pointers can be used to implement complex algorithms that require precise control over memory allocation and deallocation.

In essence, pointers act as a bridge between the high-level abstractions of C programming and the underlying hardware. They allow programmers to harness the full power of the computer's memory system, enabling the creation of efficient and dynamic code that can adapt to various situations and requirements. As a result, understanding pointers and their proper usage is essential for any C programmer looking to write optimized and versatile software.


How to declare and initialize pointers

#include <stdio.h>

int main() {
    int num = 10;
    int *numP;

    numP = &num;

    printf("Value of num: %d\n", num); // 10
    printf("Address of num: %p\n", &num); // address of num in the stack
    printf("Value of numP: %p\n", numP); // address of num in the stack

    return 0;
}

To declare a pointer in the C programming language, you need to follow a few simple steps. First, you must specify the type of data that the pointer will be pointing to. This is important because it helps the compiler understand how much memory should be allocated for the pointer and how to interpret the data stored at the memory address it points to. In the example provided above, the pointer is intended to point to an integer data type.

Next, you need to use the * syntax, which is a special symbol that indicates you are declaring a pointer variable. This asterisk is placed before the name of the pointer during its declaration, signaling to the compiler that the variable being declared is a pointer.

Finally, you should choose a name for the pointer. While you have the freedom to name the pointer anything you like, it is a common practice to use the same name as the variable it points to, followed by a "p" to indicate that it is a pointer. This naming convention makes it easier for you and others to understand the purpose of the pointer when reading the code.

In the example code provided, an integer variable num is declared and initialized with the value 10. Then, a pointer to an integer, numP, is declared. To initialize the pointer, the address of the num variable is assigned to it using the address-of operator &. This makes numP point to the memory location where the num variable is stored.


Dereferencing pointers

To dereference a pointer, you need to use the asterisk symbol (*) followed by the pointer's name. This process essentially retrieves the value stored at the memory address that the pointer is pointing to.

For example, in the given context, we have an integer variable num initialized with the value 10 and a pointer to an integer, numP, which is assigned the address of the num variable using the address-of operator (&). By dereferencing the pointer numP using the asterisk symbol, we can access the value stored in the memory location where the num variable is stored. This is done by writing *numP in the code, which would then return the value 10, as that is the value stored at the memory address to which numP is pointing.

#include <stdio.h>

int main() {
    int num = 10;
    int *numP = &num;

    printf("Value of num: %d\n", num); // 10
    printf("Value of num using pointer: %d\n", *numP); // 10
    return 0;
}

I understand that this concept might be confusing (I was also confused when I first learned about pointers in C). Just remember that the asterisk symbol is required for declaring pointer variables. However, when the asterisk symbol is used after the pointer has already been declared, it serves to return the value stored at the memory address to which the pointer is pointing.


Pointer arithmetic

In C, arithmetic operations on pointers are allowed, providing a powerful mechanism for navigating through memory efficiently. This feature enables developers to perform calculations with pointer variables, which can be particularly useful when working with arrays or other data structures that require direct memory manipulation.

For example, when an integer is added to a pointer, the address stored in the pointer is incremented by a number of bytes corresponding to the integer value. This allows for easy traversal through memory, as the pointer can be moved to the next memory location by simply adding the appropriate value. The size of the increment depends on the data type of the pointer, as different data types occupy different amounts of memory. For instance, if the pointer is of type int, and the size of an int is 4 bytes, adding 1 to the pointer would increment its address by 4 bytes, effectively moving it to the next int value in memory.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr; // Point to the first element of the array

    printf("Initial value: %d\n", *ptr);

    ptr++; // Increment the pointer to point to next element
    printf("Value after incrementing pointer: %d\n", *ptr);

    ptr--; // Decrement the pointer to point to previous element
    printf("Value after decrementing pointer: %d\n", *ptr);

    ptr += 3; // Add 3 to the pointer to point to the element 3 spots to the right
    printf("Value after adding 3 to pointer: %d\n", *ptr);

    ptr -= 2; // Subtract 2 from the pointer to point to the element 2 spots to the left
    printf("Value after subtracting 2 from pointer: %d\n", *ptr);

    return 0;
}

In the example provided above, we start by assigning the pointer to point to the first element of the array. This is a crucial step as it establishes a reference point for our pointer to work with. Once we have the pointer pointing to the first element, we can effortlessly employ pointer arithmetic to navigate and manipulate different elements within the array.

By incrementing or decrementing the pointer, we can traverse the array in either direction. This allows us to access and modify the values of the array elements without using the traditional array indexing method. Furthermore, we can add or subtract specific values to the pointer to directly access elements at a certain distance from the current position of the pointer.


Pointers and functions

Pointers are often utilized in C functions to facilitate pass-by-reference behavior, which is a powerful technique that allows functions to directly modify the original values of variables that are located outside their local scope. This is particularly useful in situations where a function needs to alter multiple values or work with large data structures, as it can help to avoid the overhead of copying data and improve the overall efficiency of the program.

For example:

#include <stdio.h>

void updateValue(int *ptr);

int main() {
    int num = 10;
    int *ptr = &num;

    printf("Before update: %d\n", num); // 10
    updateValue(ptr);
    printf("After update: %d\n", num); // 15

    return 0;
}

void updateValue(int *ptr) {
    *ptr = *ptr + 5;
}

Conclusion

Pointers are a fundamental and crucial aspect of C programming that provides access to powerful capabilities, such as dynamic memory management and efficient data manipulation. They serve as a bridge to understanding and managing memory allocation, which is a key skill for any C programmer. While pointers may initially seem complex and challenging to grasp, mastering them is essential for becoming a proficient and skilled C programmer.

To truly harness the potential of pointers in C, it is important to invest time in practice, exhibit patience, and develop a clear understanding of memory management concepts. As you gain experience working with pointers, you will begin to appreciate their versatility and the numerous advantages they offer in terms of performance optimization and code flexibility.

For example, using pointers allows you to directly manipulate memory addresses, enabling you to pass large data structures to functions more efficiently. This can lead to significant performance improvements, as you no longer need to create copies of large data structures when passing them between functions. Additionally, pointers enable you to allocate and deallocate memory dynamically, which can be particularly useful when working with data structures that change in size during the execution of a program.

In conclusion, pointers are an indispensable tool in the C programming language that offers a range of powerful capabilities. By dedicating time to practice and developing a deep understanding of memory management, you can unlock the true potential of pointers and become a more proficient C programmer. Embrace the challenge of learning pointers, and you will be rewarded with a valuable skill set that can greatly enhance your programming abilities.

If you have any lingering questions or suggestions for an article you want me to cover in the future, feel free to leave a comment in the comment section below.

Lastly, make sure to follow my newsletter so you never miss out on when I post new content! When you subscribe to my newsletter, you'll be able to read my articles straight from your inbox as soon as they're released.


a person typing on a laptop on a desk

This article is part of a series called Bit by Bit, a series devoted to all things programming. Whether you're still a computer science undergrad or the CTO of Apple, there's something for you here.

New articles in this series are posted every Tuesday!

4
Subscribe to my newsletter

Read articles from Sirus Salari directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sirus Salari
Sirus Salari

Hello! My name is Sirus, I am currently enrolled at Oregon State University, and pursuing my B.S. in computer science. Before I even decided to pursue my B.S. in computer science, I had had some solid understanding in programming fundamentals from some courses I took during my first degree program, and also through self-teaching web development through Coursera, Udemy, and the Odin Project. If you're still reading, you're probably wondering what my first degree is in, and why I chose to pursue a second degree. I graduated from the University of California, Irvine in 2021 with a B.A. in psychology. When I started college, I had no idea what I wanted to do as a career, but I just knew that I was passionate about psychology. During my third year at UCI, I took a cognitive robotics course as part of my required classes for my major, and this was the first time I was ever exposed to coding, and my first time programming. I quickly fell in love with programming, and I decided to take an intro to programming course using Python during my fourth year at UCI. I continued to love and enjoy programming and learning about all the logic involved. Initially, my plan was to combine my interests for psychology and computer science into one by applying to PhD programs in an extremely niche field called "computational neuroscience". Essentially, this field focuses on mathematical/theoretical models of consciousness, the engineering of brain-computer-interfaces, and artificial intelligence/machine learning. I was denied from all 8 of the PhD programs I applied to. Instead of being defeated, I decided to continue pursuing my passion for programming. I started with just toying around with web development using HTML and CSS, then learned JavaScript, started making project websites, learned a lot from a Coursera course and Udemy course on web development, and learned a lot from following the Odin Project curriculum. Even though my passion and interest in programming never died, I found it hard to find the motivation to continue self-teaching. Ultimately, I still wanted to find a career that would involve programming. As a result, I started researching different options for people who wish to receive a second degree in computer science. I found Oregon State University as the perfect match for my needs, as it's entirely virtual while still having the same content as in-person classes, it's affordable compared to other options, and it allowed me to transfer all of my general education requirements from UCI, saving me tons of money and time on starting all over with another degree.