Pointers in C: A Beginner’s guide
I am going to tell you a story about how pointers almost made me “regret” choosing C over any high-level language during my first months in tech. After sleepless nights pondering over which language to start with, I had inspiration from Harvard’s CS50 program and other bootcamps whose introductory lessons began with C over languages such as Python and Java.
The common reason being that starting out with C helps any student grasp the core components of computers, understand memory management, and coding since most modern languages were built on top of C. A popular saying is “C is the mother of all modern languages”.
I had aced my foundational lessons in C until Pointers was introduced to the class. Oh boy! Pointers was extremely difficult for me to comprehend in terms of a firm understanding of how computer memory works. This seemed like a herculean task for someone who had no experience with computers and no computer science background. Guess what? I almost threw in the towel! So what happened? Thanks to one of my favorite channels on YouTube The concepts of pointers were well taught.
Today, I am here to tell you to relax because almost every programmer I know has a hard time with pointers. And understanding the basic concept will make you feel like a pro. By the end of the next few sections, I guarantee you will be a pro.
What is a pointer? A pointer is a variable that stores a memory address. Let’s declare variables in our main
function. With this, we set the tone to explore how beautiful pointers are.
#include <stdio.h>
/**
* main()- defines our variable and returns and integer
* @num - variable
*/
int main()
{
int num = 5;
printf("The address and value of num is: %x %d", &num, num);
return 0;
}
This code declares an integer variable num
with a value of 5. The printf
function is used to display the memory address and value of num
. The &
operator is used to get the address of num
, and the %x
format specifier is used to display the address in hexadecimal format. The %d
format specifier is used to display the value of num
. Before we run and output the code above, we need to know what is going on under the hood as we declare our variables.
In C, memory is allocated to variables when they are declared. When you declare a variable, the compiler reserves a block of memory in the computer’s RAM to store the variable’s value. The size of the memory block depends on the data type of the variable. For example, a char
variable requires 1 byte of memory, while an int
variable requires 4 bytes of memory. Every value stored in the computer’s memory has a corresponding memory address, and this address can be used to access and manipulate the value stored at that location.
For assumption sake, we can represent the above figure to how memory is reserved by the compiler when a variable is declared and value is stored. The underlisted steps illustrate what happens under the hood and explains the above figure:
- The compiler reserves a block of memory in the computer’s RAM to store the value of
num
. The size of the memory block is 4 bytes (on most modern architectures), which is the size required to store anint
value. - The compiler assigns the value
5
to the memory block reserved fornum
. - The compiler assigns a unique memory address to the memory block reserved for
num
. This memory address can be thought of as a label that points to the location in RAM where the value ofnum
is stored. Since every byte has an address, in our case we can assume the address is 1000 for the first byte and 1004 for the last byte. Keep in mind that the base address is 1000 and can be used to represent the memory location. - The compiler associates the variable name
num
with the memory address assigned to the memory block reserved fornum
.
Now, what do we do when we want to manipulate data stored at address 1000?
This is where Pointers come in. It allows us to manipulate data stored at that address, including reading and writing to it. In C, a pointer is declared by placing an asterisk (*) in front of the variable name. Assigning the address of one variable to the pointer gives you the freedom to traverse data in a block of memory.
Let’s declare a pointer and assign the value of stored at num’s address to it.
#include <stdio.h>
/**
* main()- defines our variable and returns and integer
* @num - variable
* @ptr- store the address of num
*/
int main()
{
int num = 5;
int *ptr;
ptr = #
printf("The address and value of num is: %x %d\n", &num, num);
printf("The value and address stored at ptr is: %d %x\n", *ptr, ptr);
return 0;
}
This is what happens under the hood when the above code is compiled:
- The compiler reserves a block of memory in the computer’s RAM to store the pointer
ptr
. The size of the memory block is 4 bytes (on most modern architectures), which is the size required to store a pointer. - The compiler assigns the memory address of
num
toptr
using the&
operator. The lineptr = #
means that the memory address ofnum
is being assigned to the pointerptr
. - So, the code creates two variables:
num
, which is anint
variable with the value5
, andptr
, which is a pointer that stores the memory address ofnum
. The value ofnum
can be accessed and manipulated using either the variable namenum
or the pointerptr
.
Now let’s do an actual compilation of the code above to determine the outcome:
I know probably you are asking yourself how the value stored in int num
can be accessed or changed. Great! To access the contents stored, we dereference the pointer. Dereferencing a pointer means accessing the value stored at the address it points to. To do this, you use the asterisk (*) operator in front of the pointer variable name.
int num = 5;
int *ptr; //initialise the pointer variable
ptr = # //assign the address of num to the pointer.
Do you remember the above snippet from our main code? We can assess the value of 5 assigned to num using the dereference operator. Likewise, the value assigned to num can be changed to any other integer value.
#include <stdio.h>
/**
* main()- defines our variable and returns and integer
* @num - variable
* @ptr- store the address of num
*/
int main()
{
int num = 5;
int *ptr;
ptr = #
*ptr = 6; //this assigns the value of 6 to the num
printf("The value and address stored at ptr is: %d\t %x\n", *ptr, ptr);
return 0;
}
Dereferencing the pointer above will now assign the value of 6 to num. Why should this be possible?
Dereferencing the pointer ptr
to access the value of num
is possible because a pointer stores the memory address of a variable. When you dereference a pointer, you're accessing the memory location pointed to by the pointer and reading or modifying the value stored there.
In this case, the line *ptr = 6;
dereferences the pointer ptr
to access the value of num
. Since ptr
holds the memory address of num
, dereferencing ptr
is equivalent to accessing num
directly. So, this line is equivalent to writing num = 6;
, which means that the value of num
is being changed to 6
.
Let’s compile the code above to see the outcome:
One of the reasons why I love pointers is how easy to pass a pointer to a pointer. Passing a pointer to a function is similar to passing any other type of variable. You simply pass the pointer variable as an argument to the function.
#include <stdio.h>
/**
* main()- defines our variable and returns and integer
* @num - variable
* @ptr- store the address of num
* @increment-function that increases the value of ptr
*/
int* increment(int *ptr);//function prototype
int main()
{
int num = 5;
int *ptr;
ptr = increment(&num);
printf("The value stored at ptr is: %d\t", *ptr);
return 0;
}
/*Remember that to return a pointer from a
* function, you need to declare the function
* to return a pointer type
*/
int* increment(int *ptr)//take notice of the return type of the function
{
(*ptr)++;
return ptr;
}
In this example, the increment()
function takes a pointer to an integer as an argument. It then increments the value stored at the address pointed to by the pointer.
Pointers are a fundamental concept in C programming that allow you to manipulate data stored in memory. Understanding pointers is essential for efficient memory management and is a crucial part of C programming. This article has provided a beginner’s guide to pointers in C, including how to declare, assign, dereference, pass, and return pointers. With this knowledge, you can now start using pointers effectively in your C programs.
Thanks for reading and Happy coding!
I believe you absolutely enjoyed this article. I also enjoyed putting this piece together. However, if you feel that you need more resources on concepts such as variable declaration and functions in C, here is my go to resource for you. https://pll.harvard.edu/course/cs50-introduction-computer-science?delta=0