Pointers In C - Definition, Notation, Types and Arithmetic
Tags: tutorial, c, codenewbie, beginners
Published: 2020-07-23 08:19:30


Pointers are arguably the most difficult feature of C to understand. But, it is one of the features which make C an excellent language. In a series of articles, we will take a closer look at pointers and how they can be used in C programming.

---
###Topics - 
0.[ What exactly are pointers?](#what)
1.[ Pointer Notation and Definition](#definition)
2.[ Some Special Pointers](#special)
3.[ Pointer Arithmetic](#arithmetic)

---

###0. What exactly are pointers?<a name = "what"></a>

Before we go to the definition of pointers, let us understand what happens when we write 
```c
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. However, we can access this address using `&` (ampersand) or **address of** operator like below.

```c
printf("The address of digit = %d.",&digit);
 /* prints "The address of digit = 24650. */
```

Now, we can get the value of the variable `digit` from its address using another operator `*` (asterisk), called **indirection** or **dereferencing** or **value at address** operator.

```c
printf("The value of digit = %d.", *(&digit);
 /* prints "The value of digit = 42. */
```

###1. Pointer Definition and Notation<a name = "definition"></a>

The address of `digit` can be stored in another variable known as a pointer variable. The syntax for storing a variable's address to a pointer is :

```c
dataType *pointerVariableName = &variableName;
```

For our `digit` variable, this can be written as :

```c
int *addressOfDigit = &digit;
```
or
```c
int *addressOfDigit;
addressOfDigit= &digit;
```
This can be read as - *A pointer to `int` (integer) `addressOfDigit` stores the `address of(&)` `digit` variable.*

####Few points to understand -####

1.`dataType` - We need to tell the computer what is the data type of the variable 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.
 ```c       
int variable1;
int variable2;
char variable3;
int *addressOfVariables;
```
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.

2.`*` - 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 address, we use `*`, as a symbol in the declaration.

Now, we can use our `addressOfDigit` pointer variable to print the address and the value of `digit` as below :
```c
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` is to be read as *the value at the address stored in `addressOfDigit`.*

Notice, we used `%d` as the **format identifier** for `addressOfDigit`. Well, this not completely correct. The correct identifier to be used would be **`%p`**.

Using `%p`, the address is displayed in 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.

```c
int num = 5;
int *p = &num;
printf("Address using %%p = %p",p);
printf("Address using %%d = %d",p);
printf("Address using %%o = %o",p);
```

The output according to compiler I'm using is - 

```c
Address using %p = 000000000061FE00
Address using %d = 6422016
Address using %o = 30377000
```

>This is the warning shown when you use `%d` - 
```c
warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *'
```

###2. Some Special Pointers<a name="special"></a>

####1. Wild Pointer

```c
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 i.e. 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.


####2. Null Pointer

Now, to ensure we do not have a wild pointer, we can initialize a pointer with a `NULL` value, making it a **null pointer**.
```c
char *alphabetAddress = NULL /* Null pointer */ 
```
A null pointer points at nothing, or at a memory address that users can not access.

####3. Void Pointer####

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 as
```c
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.

```c
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.

####4. Dangling Pointer

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. 

```c
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.

###3. Pointer Arithmetic<a name = "arithmetic"></a>

We know by now that pointers are not like any other variable. They do not store any value but the address of memory blocks. Hence, it is quite clear that not all arithmetic operations would be valid with them. Would multiplying or dividing two pointers (*having address*) make sense?

####Pointers have few but immensely useful **valid** operations -

1.you can assign the value of one pointer to another only if they are of the same type (unless typecasted or one of them being `void *`.

```c
int ManU = 1;
int *addressOfManU = &ManU;
int *anotherAddressOfManU = NULL;
anotherAddressOfManU = addressOfManU; /* Valid */
double *wrongAddressOfManU = addressOfManU; /* Invalid */
```

2.you can only **add or subtract integers** to pointers.

```c
int myArray = {3,6,9,12,15};
int *pointerToMyArray = &myArray[0];
pointerToMyArray += 3; /* Valid */
pointerToMyArray *= 3; /* Invalid */
```

When you add (or subtract) an integer (say n) to a pointer, you are **not** actually adding (or subtracting) the integer literally. You are *adding (or subtracting) n-times the size of the data type of the variable the pointer is pointing to*.

```c
int number = 5;
 /* Suppose the address of number is 100 */
int *ptr = &number;
int newAddress = ptr + 3;
 /* Same as ptr + 3 * sizeof(int) */
```
The value stored in `newAddress` will **not** be 103, rather `112`. 

3.**subtraction and comparison of pointers** is valid only if both are members of the same array.

```c
int myArray = {3,6,9,12,15};
int sixthMultiple = 18;
int *pointer1 = &myArray[0];
int *pointer2 = &myArray[1];
int *pointer6 = &sixthMuliple;
 /* Valid Expressions */
if(pointer1 == pointer2)
pointer2 - pointer1;
 /* Invalid Expressions
if(pointer1 == pointer6)
pointer2 - pointer6
```

The subtraction of pointers results in the number of elements separating them.

4.you can assign or compare a pointer with `NULL`. 

>*The only exception to above rules is that the address of the first memory block after the last element of an array follows pointer arithmetic*.

Pointer and arrays exist together. There is a reason why the most valid manipulations of pointers can only be done with arrays. We will discuss the above rules with arrays in the next article.

***
*P.S. - Try to find what will be the value stored in `pointerToMyArray` for valid operations in 1 and 2, if the address of myArray[0] is `100`.*

The edits suggested by @pentacular have been included. Thanks to him/her.