Pointers are one of the most important concepts to understand in low-level programming. I remember trying to wrap my head around it with no luck. So, I wrote this article for beginners.
Everything is hard before it is easy — Goethe J.W.
So let’s just take the challenge and go to the next level.
Outline of the Article —
Digging Into Variable
Now Let’s Target the Pointers
Why Do We Need Pointers
Let’s Break the Confusion
Final Tips and Takeaway
Digging Into Variable
Whenever we create a variable, we must assign it a name, a data type, and a memory address.
Name is used to call or use the variable. A data type defines what kind of values a variable can take, ex — int
is used to store integers and char
is used to store characters. A memory address is usually a hexadecimal physical address of a variable, it is the location in the memory where the variable is stored.
Now Let’s Target the Pointers
A pointer is a variable, used to store the memory address of another variable.
The pointer stores the address of the first byte of a variable, as we know the size of a variable, the rest of the bytes are easy to calculate.
Declaring pointers in C
Astrix ***
** symbol is used to declare pointers. The declaration looks like this-
int *p;
and we declare variables like this — int v;
We can now say that p
is a ‘pointer to int’, because of the type int
. It will store the address of integer variables.
All the pointers are of the same size because they just store a memory address, which does not change for a machine. However, different data types require different pointer declaration.
char * character;
double * number;
float * balance;
This was just the declaration. Right now they do not store any value. To do that, we must provide them with the address of another variable to store. In C, the ampersand symbol &
AKA ‘address-of operator’ is used to get the address of a variable.
int a = 1000; // a stores the value 1000
int *p = &a; // p stores the memory address of a. NOT 1000
// Another Way
int a = 1000;
int *p;
p = &a; // Notice * is not used again. We only use it while declaring.
Dereferencing Pointers
Once a pointer is declared, the next step is to use it. We need to refer to the value it points to, known as the target of the pointer. This is known as dereferencing the pointer. To do this, we use the Astrix symbol *
.
int * ptr; // declaring a pointer to int
int a = 6; // a stores the value 6
ptr = &a; // p stores the memory address of a. NOT 6
printf("%d", *ptr); //prints 6. Astrix is used to dereference the pointer
Null Pointers
A null pointer is a special pointer with a value 0. 0 is the only integer value you can assign to a pointer, arbitrary values will give an error. A null pointer is a placeholder you can use to initialize a pointer later. Here’s how to set null pointer —
int *p = 0; //Works fine
int *z = 100; //Error
int a = 420;
p = &a; //Works fine. Now P stores the address of a
Why Do We Need Pointers
Pointers are essential for dynamic memory allocation. Pointer provides indirect access to a variable that can make the passing of a variable to a function much more efficient.
When we pass a variable to a function, it is copied again as a function parameter. Ex-
int a = 10;
int func(int b){
//Some operation
}
func(a);
In the above example, the value of **a
is copied to `b*. ***If we assume this program is running a 32-bit machine,***
a** will be allocated 4 bytes and
**b` *will also be allocated 4 bytes.
In this case, there are no issues because the variable is not taking a lot of space. But if we do the same thing with an array that stores a lot of values, we can hit a roadblock.
Here come pointers. Pointers enable us to pass the variable indirectly and as the address of a variable is just 1 byte, it is much more efficient. Here’s how it is done-
int sum(int *a, int *b){
int ans = *a + *b;
return ans;
}
void main(){
int a = 100;
int b = 200;
printf("%d", sum(&a,&b)); //prints 300
}
Let’s Break the Confusion
Photo by Emily Morter on Unsplash
Confusion in declaration
- Three methods
int *p;
int * p;
int* p;
Which one is correct?
All of them are correct. Different textbooks/programmers use different notation.
- In the same line
Another confusion while declaration can arise, when you try to declare *multiple pointers in the same line*. This is a common practice for declaring variables, here’s how variable declaration in a single line looks like-
int a, b, c, d; // Works fine. All of them are variables of type int
So we might think this is correct-
int * x, y, z, g; // X is the pointer to int, but y, z, and g are variables of type int.
But the right way to do it is this-
int *x, *y, *z, *g; // All of them are pointer to int
- Pointer to array
We might think pointing to an array must be the same as pointing to any other variable. But it is not the case. Let’s have a look.
// Pointer to int
int a = 1000; // variable of type int
int *p; // declaring a pointer to int
p = &a; // initialing pointer, assigning value to it
// Pointer to array
int* pta;
int a[] = {1,2,3,4}; //Creating an array
pta = a; //Notice we do not need '&'
This is because **a
**is itself a pointer to the first element of the array. Similarly, &
is not used while working with string because the string is simply an array of characters.
Confusion and errors while dereferencing pointers
- Confusion with Astrix
When we declare a pointer, we use***
but after that whenever it is used, we use it just for dereferencing.**
If you do not see ‘int’ being used with
*
then the pointer is dereferencing. ALWAYS.
Let’s see an example. Assume a is at the location 8999 in the memory.
int a = 100; //creating an integer variable
int *p = &a; // creating a pointer to int
printf('%p',p); //prints 8999
printf('%d',*p); // prints 100
- Segmentation Fault
The job of a pointer is to refer to some address in the memory space, which is divided into segments for different operations. Each of these segments has its own purpose and some are off-limits, not to be accessed by our program.
If a pointer is pointing to an off-limit memory location, then it will not have a valid target. It will crash the program with a segmentation fault error.
A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed
Here’s how it can happen
int *p; //declaring a pointer
printf('%d',*p); //dereferenging a pointer with no value. Segmentation fault.
- Passing a pointer to a function
It is very tempting to use a pointer to refer to a variable in the function, and why not, it brings a lot of perks. But if not implemented correctly, it can be a nightmare for your program. Look at this example —
void sum(int *a, int *b){
*a = *a + *b;
}
void main(){
int a = 100;
int b = 200;
sum(&a,&b);
printf(“%d”, a); //Prints 300
}
When we pass a pointer to a function, we are passing the memory location of a variable. The memory address can be utilized to permanently alter the value of a variable, as in the above example.
- Dereferencing Null Pointer
Dereferencing a null pointer will cause a segmentation fault error. To prevent the error it is often good to test for a valid target.
int* p = 0;
if (p != 0 ) {
printf("%p",*p) //Dereferencing only if the condition is met
}
Final Tips and Takeaway
Pointers are confusing, but they are one of the most crucial weapons in your arsenal, so use them wisely.
If you feel confused or do not understand what is going on, try to draw diagrams, pointers pointing to variables. Note down the values of the pointer and the value of the target.
Don’t worry if you still don’t get it, you always have the above section to clear your confusion. Below is a cheat sheet, copy it, and forget worrying about pointers.
Takeaway
Pointer provides a way to access a variable indirectly
Pointer allows you to manage your memory
Use
**int *
to declare a pointer and `*`** to dereference it.To declare multiple pointers in the same line use
*
before each one of themUse
**&
** to get the address of a variableArrays, strings, and other multi-value types do not require
**&
**to get the addressDereferencing a pointer with no value or a null pointer results in a segmentation fault
The memory address can be utilized to permanently alter the value of a variable, so be careful
I hope it was helpful. For questions, queries, or requests, use the link in profile to reach me via any social media platform.