What are pointers in C and how do they work?
Tags: Pointers, Free, C Programming, Memory Allocation, Programming

What are pointers in C and how do they work?

A pointer is a data value that references a unit of storage.

Let’s say we want to create some pointers to some blocks of storage that contain integers and the easiest way to to do in the C language would be as follows:

First we must have the following declaration in bold at the top of the .c file:

typedef int *pointerToInteger;

Here is how the .c file should look at this point: #include <stdio.h>

typedef int *pointerToInteger; int main(void){

return 0;

}

This typedef is a keyword used in C language to assign alternative names to existing datatypes.

Notice the asterisk (*) in the typedef? The * appears before the pointerToInteger to indicate to the compiler that the type pointerToInteger consists of a pointer to an int.

Now that we have a type named “pointerToInteger” defined let’s declare some variables:

#include <stdio.h>

typedef int *pointerToInteger; int main(void){

pointerToInteger A, B;

return 0;

}

Also, I would like to point out that we can achieve the same implementation by doing the following: int *A, *B;

The latter will have the same exact effect as: pointerToInteger A, B; declaration.

At the time the C compiler compiles the code, the compiler will constrain the values assigned to the A and B variables to be pointers to integers.

A and B have to contain pointers to other storage units Other storage units to which the following pointers A and B refer must contain integers

Assuming those conditions are met and the rest of your code is error free, the compiler will successfully compile your code. In the event the constrains mentioned above are not met the compiler will throw errors.

Let’s now compile the code by running the following command:

gcc -o pointer pointer.c

At this point the compilation should be successful and no errors should be present.

Now, let us compile the following code and see what the compiler is going to do:

#include <stdio.h>

typedef int *pointerToInteger; int main(void){

pointerToInteger A, B;

A = 5; // <----- Notice that we are assigning A and not *A return 0;

}

Upon compiling the program, the compiler threw the following error:

warning: incompatible integer to pointer conversion assigning to ‘pointerToInteger’ (aka ‘int *’) from ‘int’ [-Wint-conversion]

The C compiler will not compile the program because of a “type mismatch” error. The right hand side of the assignment is a value, 5, of type integer; however, the left hand side of the assignment is a variable that is required to contain a pointer to an integer, therefore the assignment is not proper.

In C, an int and a pointer to an int are not considered the same type therefore the compiler will consider this assignment an “illegal pointer arithmetic”.

Now if you attempt to compile the program below, the compiler should complete the compilation.

int main(void){

pointerToInteger A, B;

*A = 5;

return 0;

}

To print the hexadecimal value, indirect value and also the memory address of the pointer run the following program:

#include <stdio.h>

typedef int *pointerToInteger; int main(void){

pointerToInteger A, B;

*A = 5; printf("The indirect value of A is: %d

", *A);

printf("The hexadecimal value of A is: %p

", A);

printf("The hexadecimal memory address of A is: %p

", &A); return 0;

}

The program output is as follows:

Dynamic Storage Allocation

In order to create pointers to some empty integer storage units and to make A and B point to them, we will use the dynamic storage allocation procedure provided by the C language known as malloc().

Let’s look at the following program:

#include <stdio.h>

typedef int *pointerToInteger; int main(void){

pointerToInteger A;

A = (pointerToInteger)malloc(sizeof(int)); printf("The indirect value of A is: %d

", *A);

printf("The hexadecimal value of A is: %p

", A);

printf("The hexadecimal memory address of A is: %p

", &A); return 0;

}

Program output:

This program creates a block of storage for an integer and place a pointer to it in A. (pointerToInteger) is the syntax to type casts the void pointer returned by the malloc function into a pointer to a block of storage containing an integer.

At this point, we have defined a variable A which is a pointer to an empty integer unit storage and we have seen that the value of A is 0.

To store an integer in the block of storage referenced by the pointer in variable A we will have to do the following:

*A = 1;

The * preceding variable A will be an indication that the value 1 will need to be stored in A’s referent.

Now let’s do the same thing for B by allocating the number of bytes to store in an int. Then let’s store the value of 25 in the block of storage referenced by the pointer in variable B.

B = (pointerToInteger)malloc(sizeof(int));

*B = 25;

The code should now look like this:

#include <stdio.h>

#include <stdlib.h> typedef int *pointerToInteger; int main(void){ pointerToInteger A, B; A = (pointerToInteger)malloc(sizeof(int));

*A = 1; printf(“The indirect value of A is: %d

”, *A);

printf(“The hexadecimal value of A is: %p

”, A);

printf(“The hexadecimal memory address of A is: %p

”, &A); B = (pointerToInteger)malloc(sizeof(int));

*B = 25; printf(“

”);

printf(“The indirect value of B is: %d

”, *B);

printf(“The hexadecimal value of B is: %p

”, B);

printf(“The hexadecimal memory address of B is: %p

”, &B); return 0;

}

Run the program and the output will be as such:

How to copy pointers?

Now, let’s take this a step further and let’s make copy of pointers. To perform such copy we need to do the following assignment: A = B;

Notice that we are not using the * when performing the assignment. (We will come back to that in the next step)

A copy of the value of B which is a pointer to the block of storage containing 25 is taken and placed in A so that A now points to the same block of storage that B points to. This is called pointer copying.

Below is a diagram of the assignment operation.

Let’s look at the program:

#include <stdio.h>

#include <stdio.h>

#include <stdlib.h> typedef int *pointerToInteger; int main(void){ pointerToInteger A, B; A = (pointerToInteger)malloc(sizeof(int));

*A = 1; printf(“The indirect value of A is: %d

”, *A);

printf(“The hexadecimal value of A is: %p

”, A);

printf(“The hexadecimal memory address of A is: %p

”, &A); B = (pointerToInteger)malloc(sizeof(int));

*B = 25; printf(“

”);

printf(“The indirect value of B is: %d

”, *B);

printf(“The hexadecimal value of B is: %p

”, B);

printf(“The hexadecimal memory address of B is: %p

”, &B); A = B; printf(“

”);

printf(“The indirect value of A is: %d

”, *A);

printf(“The hexadecimal value of A is: %p

”, A);

printf(“The hexadecimal memory address of A is: %p

”, &A); return 0;

}

Program output:

Notice that both A and B are nw pointing to the same reference and we can validate this by verifying that the hexadecimal value for both A and B are identical.

Now, let’s change the value of the block of storage that B points to from 25 to 66 by doing the following assignment:

*B = 66;

What do you think will happen? What will B value be? Will the value of A remain 25? If not, what should it be?

First, let’s look at how the program looks:

Program output:

If your answer was 66, then you have guessed correctly. The reason the integer value of A became 66 when printing it out to the terminal is because A points to B and since we changed the value in the block of storage that B points to, A will also reflect that change.

How to copy integers?

In the previous section we have seen how to properly make a copy of a pointer, in this section we will see how to make a copy of the integer value.

Let’s use the same program from the previous step except let’s make one smal change. On line 21 in the screen shot shown below, let’s use the * when performing the assignment: *A = *B;

Program output:

You will notice that the value in the block of storage that A points to remains 25 even thought the value in the block of storage that B points to has changed to 66 from 25. That is because we have made a copy of the integer rather then copying the pointer. We did that by using the *.

Inaccessible Storage

Remember the following example where we made a copy of pointer B and assigned it to A?

Well, in that example the block of storage containing the integer 1 has no pointer pointing to it any longer and in that scenario that block of storage has become inaccessible. This means that nobody can access it to either read its value or to store a new value into it.

We have to be mindful of such scenarios because it is wasteful to allocate blocks of storage and then abandon them when they become inaccessible.. The pool of storage for allocating new blocks of storage could easily become used up. If such scenario occurs this would lead to a storage allocation failure and when we call the function malloc() in our program, there would be no more storage to allocate thus causing the program to fail.

How to guard against storage allocation failure?

To safe guard against this storage allocation failure, the programming language C offers storage reclamation function free(X).

The free(X) function is designed to return the block of storage to which the pointer in X points back to pool of unallocated storage which will make it available for usage again.

Inaccessible storage is sometimes referred to as garbage in the C language. Managed languages such as C# .NET, the framework has procedure for storage reclamation called garbage collection procedure which is triggered automatically when available unallocated storage is used up. Unlike managed languages, C programming language does not have automatic garbage collection and therefore must be explicitly managed by the programmer. It is incumbent upon the programmer to perform garbage collection in the program.

To free up inaccessible blocks of storage we simply execute the free(X) function immediately prior of the reassignment. The following example is an illustration on how to properly do it:

#include <stdio.h>

#include <stdio.h>

#include <stdlib.h> typedef int *pointerToInteger; int main(void){ pointerToInteger A, B; A = (pointerToInteger)malloc(sizeof(int));

*A = 1; B = (pointerToInteger)malloc(sizeof(int));

*B = 25; /*

This will ensure that the current block of storage

that A points to will be free up and the block of storage will

be returned to the pool of blocks of storages

*/

free(A); A = B; return 0;

}

Conclusion

In conclusion, I hope that this article was beneficial to you. Before I leave, I would like to leave you with the following program. What should this program output?

Try to think about it before compiling it and running it. Try to figure it out on your own first!