Pointers are arguably the most difficult feature of C to understand. But, they are one of the features which make C an excellent language.
In this article, we will go from the very basics of pointers to their usage with arrays, functions, and structure.
So relax, grab a coffee, and get ready to learn all about pointers.
Before we get to the definition of pointers, let us understand what happens when we write the following code:
int digit = 42;
A block of memory is reserved by the compiler to hold an int
value. The name of this block is digit
and the value stored in this block is 42
.
Now, to remember the block, it is assigned with an address or a location number (say, 24650).
The value of the location number is not important for us, as it is a random value. But, we can access this address using the &
(ampersand) or address of operator.
printf("The address of digit = %d.",&digit);
/* prints "The address of digit = 24650\. */
We can get the value of the variable digit
from its address using another operator *
(asterisk), called the indirection or dereferencing or value at address operator.
printf("The value of digit = %d.", *(&digit);
/* prints "The value of digit = 42\. */
The address of a variable can be stored in another variable known as a pointer variable. The syntax for storing a variable’s address to a pointer is:
dataType *pointerVariableName = &variableName;
For our digit
variable, this can be written like this:
int *addressOfDigit = &digit;
or like this:
int *addressOfDigit;
addressOfDigit= &digit;
This can be read as - A pointer to _int_
(integer) _addressOfDigit_
stores the _address of(&)_``_digit_
variable.
dataType
– We need to tell the computer what the data type of the variable is whose address we are going to store. Here, int
was the data type of digit
.
It does not mean that addressOfDigit
will store a value of type int
. An integer pointer (like addressOfDigit
) can only store the address of variables of integer type.
int variable1;
int variable2;
char variable3;
int *addressOfVariables;
*
– A pointer variable is a special variable in the sense that it is used to store an address of another variable. To differentiate it from other variables that do not store an address, we use *
as a symbol in the declaration.
Here, we can assign the address of variable1
and variable2
to the integer pointer addressOfVariables
but not to variable3
since it is of type char
. We will need a character pointer variable to store its address.
We can use our addressOfDigit
pointer variable to print the address and the value of digit
as below:
printf("The address of digit = %d.", addressOfDigit);
/* prints "The address of digit = 24650." */
printf("The value of digit = %d.", *addressOfDigit);
/*prints "The value of digit = 42\. */
Here, *addressOfDigit
can be read as the value at the address stored in _addressOfDigit_
.
Notice we used %d
as the format identifier for addressOfDigit
. Well, this is not completely correct. The correct identifier would be %p
.
Using %p
, the address is displayed as a hexadecimal value. But the memory address can be displayed in integers as well as octal values. Still, since it is not an entirely correct way, a warning is shown.
int num = 5;
int *p = #
printf("Address using %%p = %p",p);
printf("Address using %%d = %d",p);
printf("Address using %%o = %o",p);
The output according to the compiler I’m using is the following:
Address using %p = 000000000061FE00
Address using %d = 6422016
Address using %o = 30377000
This is the warning shown when you use %d - " warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ ".
char *alphabetAddress; /* uninitialised or wild pointer */
char alphabet = "a";
alphabetAddress = &alphabet; /* now, not a wild pointer */
When we defined our character pointer alphabetAddress
, we did not initialize it.
Such pointers are known as wild pointers. They store a garbage value (that is, memory address) of a byte that we don’t know is reserved or not (remember int digit = 42;
, we reserved a memory address when we declared it).
Suppose we dereference a wild pointer and assign a value to the memory address it is pointing at. This will lead to unexpected behaviour since we will write data at a memory block that may be free or reserved.
To make sure that we do not have a wild pointer, we can initialize a pointer with a NULL
value, making it a null pointer.
char *alphabetAddress = NULL /* Null pointer */
A null pointer points at nothing, or at a memory address that users can not access.
A void pointer can be used to point at a variable of any data type. It can be reused to point at any data type we want to. It is declared like this:
void *pointerVariableName = NULL;
Since they are very general in nature, they are also known as generic pointers.
With their flexibility, void pointers also bring some constraints. Void pointers cannot be dereferenced as any other pointer. Appropriate typecasting is necessary.
void *pointer = NULL;
int number = 54;
char alphabet = "z";
pointer = &number;
printf("The value of number = ", *pointer); /* Compilation Error */
/* Correct Method */
printf("The value of number = ", *(int *)pointer); /* prints "The value at number = 54" */
pointer = &alphabet;
printf("The value of alphabet = ", *pointer); /* Compilation Error */
printf("The value of alphabet = ", *(char *)pointer); /* prints "The value at alphabet = z */
Similarly, void pointers need to be typecasted for performing arithmetic operations.
Void pointers are of great use in C. Library functions malloc()
and calloc()
which dynamically allocate memory return void pointers. qsort()
, an inbuilt sorting function in C, has a function as its argument which itself takes void pointers as its argument.
A dangling pointer points to a memory address which used to hold a variable. Since the address it points at is no longer reserved, using it will lead to unexpected results.
main(){
int *ptr;
ptr = (int *)malloc(sizeof(int));
*ptr = 1;
printf("%d",*ptr); /* prints 1 */
free(ptr); /* deallocation */
*ptr = 5;
printf("%d",*ptr); /* may or may not print 5 */
}
Though the memory has been deallocated by free(ptr)
, the pointer to integer ptr
still points to that unreserved memory address.
#c #developer