Back to Basics with Pointers

Everything you need to know about pointers

Back to Basics with Pointers

Photo by Åaker on Unsplash

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1627505398264/AdIWwBW7U.html) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)Photo by Emily Morter on Unsplash

Confusion in declaration

  1. Three methods
int *p;
int * p;
int* p;

Which one is correct?

All of them are correct. Different textbooks/programmers use different notation.

  1. 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
  1. 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

  1. 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
  1. 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.
  1. 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.

  1. 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 them

  • Use **&** to get the address of a variable

  • Arrays, strings, and other multi-value types do not require **&**to get the address

  • Dereferencing 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.

Did you find this article valuable?

Support Shubh Patni by becoming a sponsor. Any amount is appreciated!