(Almost) Everything You Need To Know About Pointers in C
Tags: programming, tutorial, c, pointers
Published: 2021-09-09 07:09:05

> When I was first starting out with C, pointers were something that confused me the most. I was scared of pointers and could never understand how to use them. 

No, I didn't say that! In fact, pointers have always been intuitive to me. But most of the students starting to learn C are put off by the idea of pointers. It is one of those areas of C which are not explained properly to students. resulting in many misconceptions about them.

In this huge post, I have compiled almost everything that is fundamental to pointers. Of course, it is a huge topic, and it's not possible to cover the entirety of it in one post, but once you know these fundamentals, you'll be able to use them more efficiently, and hopefully will be able to tackle pointers in a program.

Let's start.

## Beginning With Pointer Sorcery

One fine afternoon, you are lying on your couch and thinking about the year 2038 problem and the end of the universe, and suddenly your friend calls you and asks "Hey, I want to come over and contemplate our existence, but I do not know where your house is!"

You say, "No problem buddy. I'll give you a copy of my home."

 Of course, you'd never say that. Instead, you will give him your address so that he can come over. Now, you could make him a copy of your home if you're generous enough, but it takes time and defeats the purpose of your friend coming over. He wants to come to your house, not a copy.

Now think in terms of programming. At the time when C was created, memory was scarce, and being efficient was not only needed but vital. For this reason, you'd have to be really careful while dealing with memory. You'd really not like to make unnecessary copies of something.

Another case you can consider is of having "side effect" of a function. Consider this simple program.

    #include <stdio.h>
    void f(int a) { a = 10; }
    int main() {
    	int a = 5;
        printf("%d\n", a);
        f(a);
        printf("%d\n", a);
    }

which just prints

    5
    5

Even though you are calling the function `f` with the variable `a` as a parameter, and `f` is changing the value of `a`. the change doesn't show up in the original value of `a`, because when you are calling the function `f`, you are passing a copy of `a`, not `a` itself. In other terms, you are giving your friend a copy of your house.

This is desired in most cases. You don't really want your functions to accidentally change any variable where it's not supposed to. But sometimes, you actually want the function to change a variable. You have already seen such a function that can change the actual parameter.

    scanf("%d", &n);

How does `scanf` change the value of `n`? The answer is through pointers.

Also, take a look at this classic example of swap- 

    void swap(int a, int b) {
    	int t = a;
        a = b;
        b = t;
    }
    int main() {
    	int a = 5, b =10;
        swap(a, b);
        printf("a = %d, b = %d\n", a, b);
    }

It works, except it doesn't.

`swap` does swap the variables, but since you are making a copy of `a`, and `b`, the change doesn't show up outside the function. But we want the function to be able to change the actual variables. So we need to have some kind of way to pass the actual `a` and `b` . But in C, there is no way you can pass "actual" variables. (which is not the case in C++).

One way you might end up doing is to make `a` and `b` global 

    int a = 5, b = 10;
    void swap() {
    	int t = a;
        a = b;
        b = t;
    }
    int main() {
        swap();
        printf("a = %d, b = %d\n", a, b);
    }

And it now works, because `swap` now can access `a` and `b`, but having global variables is a real [bad idea](https://wiki.c2.com/?GlobalVariablesAreBad).

The way? Give `swap` the addresses of `a` and `b`. If it has addresses of `a` and `b` , it can change them directly.

Pointers are nothing but variables that hold the address of another variable.

Now, where does this address come from? We know how [bits and bytes work](https://computer.howstuffworks.com/bytes.htm). The RAM of the computer can be thought of as a mess, a really long one, with lots of rooms one after another, and each byte is a room. How does the computer know which room to put data in? It gives a number to each room, and that number is the address.

When I write

    char a;

I tell the compiler "Buddy, reserve one room in the mess, and call it `a`" . Why one room? Because the size of `char` is 1 byte. (Note that C's definition of a byte is basically the `sizeof char` , which in some rare cases might not be actually 1 byte in the machine, however, it is always 1 byte in C)

If I write

    int b;

I tell the compiler to reserve the number of rooms necessary for `int` and call it `b`.

**Side rant: People coming from Turbo C, and being told size of `int` is 2 bytes, it's not necessarily so, and probably not so in any modern computer. The C standard guarantees at least 2 bytes for `int` and on my machine `sizeof(int)` is 4, so we will stick to that for the rest of this post.**

Now that our `b` has 4 rooms, it will stay in the rooms starting from the first one. So that when we say "address of b", we actually mean "address of the starting or ending byte of b". (See [big endian and little endian](https://chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_3.html). For this tutorial, let's assume it's the ending byte because it is so on my machine)

In order to get the address of `b` and store it, we need to use a pointer variable. Just like any other variable, a pointer also has a type, defined by the type of the thing it points to. The syntax is `type_of_the_thing_it_points_to *name`

    char *pa;

Note that the asterisk need not be adjoined to the variable name. Any of these is valid - 

    char* pa;
    char *pa;
    char * pa;

We will prefer the 2nd syntax. We will see in a short while why.

Let's first see how to assign a value to a pointer. In order to make a pointer point to a variable, we have to store the address of the variable in the pointer. The syntax for getting the address of a variable is `&variable_name`.

    char a;
    char *pa = &a; // pa now contains the address of a
    
    printf("%p", pa); // %p is the format specifier to print a pointer

If you run this program, you will see something like `0x7ffc2fc4ff27`. That is the value of the pointer, which is the address of the variable `a` (this is in hexadecimal). This value is not fixed. If you run the program again, the value will likely change, because `a` will be stored somewhere else.

One thing you might have noticed. Although we are declaring it as `*pa`, the * is not used when printing the pointer. In fact, * is not a part of the name of the pointer. The name of the pointer is just `pa`. The * is instead used to get the value of whatever thing the pointer is pointing to (known as dereferencing).

    char a = 'a';
    char *pa = &a;
    
    printf("%p\n", pa); // prints the value of pa
    printf("%c", *pa); // prints the value of a

So, quickly revise - 

1. `pa` is the value of the pointer, which is the address of `a`.
2. `*pa` is the value of the thing `pa` is pointing to, in this case `a`.

One more time.

1. `pointer_name` is the value of the pointer itself.
2. `*pointer_name` is the value of the thing the pointer points to.

Now, this should be clear.

    char a = 'a', b = 'b';
    char * pa = &a; // pa points to a
    *pa = 'c'; // change the value of whatever pa is pointing to, in this case a
    printf("%c", a); //prints c
    pa = &b; // change the pointer itself. pa now points to b
    *pa = 'd' // change the value of whatever pa is pointing to, in this case b
    printf("%c", a); //prints c, because a is unchanged as pa is no more pointing to a
    printf("%c", b); //prints d

Now we can rewrite the swap function as follows - 

    void swap(int *a, int *b) {
    	int t = *a;
        *a = *b;
        *b = t;
    }

And call it with the addresses `swap(&a, &b)`. This works and the change shows up outside the function too. Because once you have the address of a variable, you know where it lives in memory so you can freely change it.

You might have a valid question. Since all pointers are just addresses, which are basically numbers, why is the type of the thing it points to necessary? Why do we distinguish between `char*` and `int*` although both of them are just some numbers?

The answer is clear. When you dereference a pointer, the compiler needs to know what data type is the object. Remember that address of a variable is just the address of the ending byte of the variable. In order to read the variable, the compiler needs to know its type so that it knows how many bytes to read.

Consider this program

    #include <stdio.h>
    
    int main() {
        int a = 1101214537;
        char *pa = &a;
        printf("%c", *pa);
    
        return 0;
    }

It prints (ignore the compiler warning)

    I

What happened here?

If you represent `1101214537` in binary it is `01000001 10100011 00110011 01001001` . So `&a` which is the address of `a` points to the byte in memory that contains the last byte of the number, which is `01001001`. When I dereference `pa`, the compiler sees that it points to `char` so it reads only one byte at that address, giving the value `01001001` which `73`, the ASCII for `I`. This is why the type is absolutely and you should not mix and match types unless you are absolutely sure of what you are doing. (We'll see a few examples)

Remember we told that we will prefer `int *pa` rather than `int* pa` although they are the same? The reason is to safeguard against the following common misconception. Can you find the difference?

    int a, b; // a and b both are int
    int* pa, pb; // whoopsie! pb is not a pointer

If you are a beginner, you will assume that since `int a, b` makes both `a` and `b` as `int` , then `int* pa, pb` will make both `pa` and `pb` as `int*`. But it doesn't. The reason is `*` "binds" to the variable name, not the type name. If instead, you'd have written

    int *pa, pb;

you'd rightly conclude `pa` is a pointer to int, and `pb` is just `int`. Hence I prefer to write the `*` with the variable name, however, there are compelling reasons for the other style as well, and if you are careful enough, you can use the other style as well.

### NULL and void pointer

These two are a special types of pointers in C. The Null pointer is used to denote that the pointer doesn't point to a valid memory location.

    int *pa = NULL;
    char *pb = NULL;

We use Null pointer in various ways, for example, to denote failure, or mark the end of a list of unknown size, etc. Dereferencing a Null pointer is undefined behavior and your program will likely crash.

Note that the Null pointer is not the same as pointer to memory address 0, although it's very likely to be so. There are exceptions, for example in small embedded devices where address 0 might be a valid location.

Void pointer is one more interesting pointer in C. Basically void pointer "throws away" the type of a pointer. It is a general-purpose pointer that can hold any type of pointer and can be cast to any type of pointer. The following are all valid - 

    int a;
    char b;
    float c;
    void *p = &a;
    p = &b;
    p = &c;

But you can't dereference a `void *` because it doesn't have a type. Trying to dereference a `void *` will give you an error. However, you can cast it to anything you want and then dereference it, although it's not a very good idea and it violates the type aliasing rules.

    int a = 65;
    void *p = &a;
    char *c = (char *) p;
    printf("%c\n", *c);

Here we're removing the type of `&a` through `p` and casting it to a `char *`. Essentially `a` is getting read as a `char` and this prints `A`.

Be careful during casting. You should use void pointers only if you are absolutely sure of what you're doing.

    int a = 65;
    void *p = &a;
    int (*f)(int) = (int (*)(int)) p; // cast as a function pointer (discussed later)
    f(2); // Segmentation fault

Sometimes you'll see `char *` used as a generic pointer as well. This is because `void *` was not present in old versions of C, and some practice remains, or maybe the code needs to do pointer arithmetic on that pointer.

Generally `void *``i`s used in places where you expect to work with pointers to multiple types. As an example, consider the famous `memcpy` function which copies a block of memory. Here is the signature of `memcpy` - 

    void * memcpy ( void * destination, const void * source, size_t num );

As you see, it accepts `void *`, which means it works with any type of pointers. As for an example (copied from [cplusplus](https://www.cplusplus.com/reference/cstring/memcpy/)) - 

    /* memcpy example */
    #include <stdio.h>
    #include <string.h>
    
    struct {
      char name[40];
      int age;
    } person, person_copy;
    
    int main ()
    {
      char myname[] = "Pierre de Fermat";
    
      /* using memcpy to copy string: */
      memcpy ( person.name, myname, strlen(myname)+1 );
      person.age = 46;
    
      /* using memcpy to copy structure: */
      memcpy ( &person_copy, &person, sizeof(person) );
    
      printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
    
      return 0;
    }

In line 15, we invoked `memcpy` with `char *` and in line 19, we invoked `memcpy` with a pointer to structure, and they both work.

### Pointer arithmetic

Since pointers are just like other variables, you'd expect that we should be able to do arithmetic with them. We can, but there's a catch. First of all, we are only allowed these 2 operations - 

1. Addition (and hence subtraction) of an integer constant to a pointer.
2. Subtraction of two pointers of the same type.

Let's see them one by one

    int a;
    int *pa = &a;
    
    printf("pa = %p\n", pa);
    printf("pa + 1 = %p\n", pa + 1);
    printf("pa - 1 = %p\n", pa - 1);

This prints

    pa = 0x7ffdd7eeee64
    pa + 1 = 0x7ffdd7eeee68
    pa - 1 = 0x7ffdd7eeee60

Strangely, it seems `pa+1` increments the pointer by 4, and not by 1. The reason lies in the datatype of the thing it points to, in this case, `int`. Remember that a pointer must always point to something. When you increment the pointer by 1, it points to the next thing.

In this case, `pa` points to an `int`. Where is the next `int`? After 4 bytes of course, because the size of int is 4 bytes.

Similarly `pa-1` points to the previous `int` which lies 4 bytes before.

By the same logic, `pa+2` points to the `int` 2 places after `a` that is `4 * 2 = 8` bytes after `a`, and `pa+n` points to the integer `n` places after `a` which is `4n` bytes after `a`.

An observant reader might have noticed that things are looking almost like an array, and he/she is not wrong completely. In a few minutes, we shall explore the idea of array using pointers. Before let's talk about the subtraction of pointers.

    int a;
    int *pa = &a;
    int *pb = pa + 2;
    printf("pa = %p\n", pa);
    printf("pb = %p\n", pb);
    printf("pb - pa = %ld\n", pb - pa);
    printf("pa - pb = %ld\n", pa - pb);

This prints

    pa = 0x7ffec09d685c
    pb = 0x7ffec09d6864
    pb - pa = 2
    pa - pb = -2  

Similar to the previous case, although the difference between `pa` and `pb` is of 8 bytes as numbers, as pointers the difference is 2. The negative sign of `pa-pb` implies that `pb` points after `pa`.

To quickly summarise - 

1. If I have `some_data_type *p`, then `pa + n` increments the pointer by `n * sizeof(some_data_type)` bytes.
2. If I have `some_data_type *p, *q` then `p - q` is equal to the difference in bytes divided by `sizeof(some_data_type)`

Let's consider what happens if we mix indirection and prefix or postfix increment/decrement operators. Can you guess what each of these does? I have omitted the data types so that you can't guess ;-). Assume `p` points to `int`

    x = *p++;
    x = ++*p;
    x = *++p;

In order to answer, you have to remember the precedence - 

1. Postfix `++` and `--` have higher precedence than `*`
2. Prefix `++` and `--` have the same precedence as `*`.

Since the `*` operator is itself a prefix, you'll never have a problem with prefix increment or decrement. You can tell just by the order of the operator. For the postfix operator, remember that postfix works first, then indirection.

So `*p++` is same as `*(p++)`. So, the value of `p` will be used in the expression, then `p` will be incremented. So `x` gets the value of `*p` and `p` becomes `p+1`, so that the type of `x` ought to be `int` too.

    int a = 5;
    int *p = &a;
    int x;
    printf("Before:\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    x = *p++;
    printf("After\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    printf("x = %d\n", x);

This prints

    Before:
    a = 5
    p = 0x7ffe82ae9eb0
    After
    a = 5
    p = 0x7ffe82ae9eb4
    x = 5

`++*p` will probably not arise confusion. This is the same as `++ (*p)`. So, first `p` is dereferenced, and then `++` is applied. So whatever `p` was pointing to gets incremented by 1 and then it is assigned to `x`, and `p` is unchanged. So the type of `x` is again `int`.

    int a = 5;
    int *p = &a;
    int x;
    printf("Before:\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    x = ++*p;
    printf("After\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    printf("x = %d\n", x);

This prints

    Before:
    a = 5
    p = 0x7fff1484e210
    After
    a = 6
    p = 0x7fff1484e210
    x = 6

And finally `* ++p` is same as `* (++p)`. So, first `p` gets incremented by 1, and then it is dereferenced. So `x` gets the value of whatever is this incremented pointer pointing to.

    int a = 5;
    int *p = &a;
    int x;
    printf("Before:\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    x = *++p;
    printf("After\n");
    printf("a = %d\n", a);
    printf("p = %p\n", p);
    printf("x = %d\n", x);

This prints

    Before:
    a = 5
    p = 0x7ffd4bad9c90
    After
    a = 5
    p = 0x7ffd4bad9c94
    x = 32765

We can also compare pointers using relational operators like `==`, `<=` etc. but there's a catch. You can only compare two pointers using `<=, <, >=, >` if they both are pointers of the same type, and of the same array or same aggregate object. Otherwise, it is undefined behavior.

Quoting C11 - 

> When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression `P` points to an element of an array object and the expression `Q` points to the last element of the same array object, the pointer expression `Q+1` compares greater than `P`. ****In all other cases, the behavior is undefined.****

Take a look - 

    typedef struct some_struct {
        int p;
        int q;
    } some_struct;
    
    some_struct a = {1, 2}; 
    int *p = &a.p;
    int *q = &a.q;
    if(p > q) puts("Hi\n");
    else puts("Bye\n");

This prints `Bye`. Well first of all this comparison is valid since `p` and `q` are both pointers to `int` and also they both point to elements of the same `struct`. Since `q` was declared later in `some_struct` , `q` compares greater to `p`

For equality, the restriction is a bit slack. You can compare any two pointers as long as they have the same type, or one of them is a null pointer or void pointer. And they compare equal if they point to the same object, or if both are null (doesn't matter if types don't match), or if both are pointing to members of the same union.

Let's demonstrate the last point.

    typedef union some_union {
        int p;
        int q;
    } some_union;
    
    some_union a;
    int *p = &a.p;
    int *q = &a.q;
    
    if(p == q) {
    	puts("Equal\n");
    } else {
    	puts("Not equal\n");
    }

This prints `Equal` although `p` and `q` point to different things, they are within the same union.

Since pointers are just numbers, can you put any integer in them? The answer is yes, but be careful of what you put. In fact, be careful when you dereference it. If you try to dereference an invalid address, your program will likely segfault and crash.

    int *x = (int *) 1;
    printf("%d\n", *x);

This instantly segfaults.

## Admitted to Hogwarts School Of Pointer Magic

### Pointers and Arrays

Let's now move to some advanced sorcery - array and pointers.

We know that an array stores its elements contiguously in memory. Which means the elements are stored in order one after another. So if we have `int arr[10]`, we know `arr[1]` lies right after `arr[0]`, `arr[2]` lies right after `arr[1]` and so on. So if I have a pointer to `arr[0]` and I increment it by 1, it should point to `arr[1]`. If I increment it by 1 again, it should point to `arr[2]`. 

In fact, there are so many similarities between arrays and pointers, that we can talk about the equivalence of arrays and pointers.

**Word of caution! This does not mean arrays and pointers are the same and you can use one in place of another. This misconception is quite common and ends up being harmful. Arrays and pointers are very different things, but pointer arithmetic and array indexing are equivalent.**

For starters, the name of an array "decays" into a pointer to the first element. What do I mean by that? Consider this code - 

    int arr[10];
    int *pa = &(arr[0]); // pointer to the first element
    int *pb = arr; // What
    
    printf("pa = %p\n", pa);
    printf("pb = %p\n", pb);

This prints

    pa = 0x7ffef7706bb0
    pb = 0x7ffef7706bb0

But aren't we mixing up datatypes in the case of `pb`? `pb` is a pointer to `int` and `arr` is an array of int!

Turns out that `arr` is *converted to* a pointer to the first element. So that `arr` and `&(arr[0])` is equivalent.

Quick note: indexing operator `[]` has higher precendence than `*` so that `* arr[0]` is same as `* (arr[0])`

Let's do even more funny stuff - 

    int arr[3] = {1, 2, 3};
    int *pa = arr;
    
    
    printf("arr[1] = %d\n", arr[1]); // 2nd element using array indexing
    printf("*(pa + 1) = %d\n", *(pa + 1)); // 2nd element using pointer arithmetic
    printf("pa[1] = %d\n", pa[1]); // What
    printf("*(arr + 1) = %d\n", *(arr + 1)); // Whatt
    printf("1[arr] = %d\n", 1[arr]); // Whattt

The first `printf` is ok. `arr[1]` means the 2nd element of `arr`.

We just reasoned about the 2nd line. `pa` points to the first element of `arr`. So `pa+1` will point to the next `int` in memory, which is `arr[1]` because array elements are stored contiguously.

But in the 3rd and 4th lines, aren't we mixing up array and pointer syntax? Well, turns out that `arr[i]` is just the same as `*(arr + i)` and this is (almost) what happens internally when you write `arr[i]`.

Similarly `*(pa + i)` is the same as `pa[i]`. Pointer arithmetic works both on arrays and pointers. Similarly, array indexing works on both pointers and arrays.

And for the last part, `arr[1]` is the same as `*(arr + 1)` which is the same as `*(1 + arr)` which should be the same as `1[arr]`. This is one of those weird quirks of C.

Does this mean you can mix and match pointers and arrays? The answer is a big fat no. The reason is although `arr[i]` and `pa[i]` give you the same result, i. e. the 2nd element of `arr`, the way they reach there is quite different.

Consider the code

    #include<stdio.h>
    int main() {
        int arr[3] = {1, 2, 3};
        int *pa = arr;
        int a = arr[1];
        int b = pa[1];
    }

Let's look at the assembly code generated by the compiler. I used [Compiler Explorer](__GHOST_URL__/p/7a2aaf27-dbd2-4822-8dd4-3bbb6da203ac/godbolt.org). Don't worry if you can't read assembly. We'll go together.

We are interested in lines 5 and 6. Here's the related assembly for `int arr[3] = {1, 2, 3}`.

    mov     DWORD PTR [rbp-28], 1
    mov     DWORD PTR [rbp-24], 2
    mov     DWORD PTR [rbp-20], 3

In case you are seeing assembly for the first time, `rbp` is the base pointer register that holds the memory address of the base of the current stack frame. Don't worry about what that means. For now think of `rbp` as a pointer variable, which points to some location in memory.

Here the contents of `arr` is being put in memory. For example, consider the first line. The `mov` instruction puts the value 1 somewhere in memory. The `DWORD PTR` tells that it is of size 32 bit or 4 bytes as it is an `int`. The syntax `[rbp - 28]` means the content of the memory location at the address `rbp-28`. Remember that `rbp` is like a pointer. So it is the same as doing `* (rbp - 28)`.

Putting everything together, we see that the first line puts the value 1 in the memory address pointed by `rbp-28`. The next value should be stored right after it, i. e. after 4 bytes. Which should be pointed by `rbp-24` and indeed that is where 2 is stored. And finally, 3 is stored in the memory address pointed by `rbp-20`.

So, we see that the address of the first element is `rbp-28`. So we'd expect this should be reflected in the line `int *pa = arr;`. And indeed it is - 

    lea     rax, [rbp-28]
    mov     QWORD PTR [rbp-8], rax

`lea` means `load effective address` which calculates `rbp-28` and stores the **address** in `rax` rather than fetching the content of the memory address  `rbp-28` and storing the *content*. In other words, it just copies the address of the first element in `rax` register and then in the memory location `rbp-8` which is our `pa`.

Now let's look at `int a = arr[1]`

    mov     eax, DWORD PTR [rbp-24]
    mov     DWORD PTR [rbp-12], eax

So here first the content of `rbp-24` is loaded into `eax` and then stored in `rbp-12` which is our `a`. The interesting thing to notice is that the compiler knows the first element of `arr` is at `rbp-28` so when you write `arr[1]` it directly offsets the base address by ` and gets `rbp-24`. This happens in compile time.

Now let's look at `int b = pa[1];`

    mov     rax, QWORD PTR [rbp-8]
    mov     eax, DWORD PTR [rax+4]
    mov     DWORD PTR [rbp-16], eax

Here we see first the value stored at `rbp-8` is moved to `rax`. Remember this was our `pa` variable? So first the value stored at `pa` is read. Then it is offset by 1, so we get `rax + 4` and we read the value at `rax+4` and store it to `eax`. Finally, we store the value from `eax` to `rbp-16` which is the `b` variable.

The noticeable difference is that it takes one extra instruction in case of pointer. Because array address is fixed, when you write `arr`, the compiler knows what you're talking about. But a pointer value can be changed. So when you write `pa`, the value of `pa` needs to be read first and then it can be used.

Now suppose something like this. You have two files. One contains a global array like

    int arr[3] = { 1, 2, 3 };

And in another file, you get carried away by the equivalence of array and pointer and write

    extern int *arr;

In other words, you have declared `arr` as a pointer but defined as an array. What will happen if you write `int a = arr[1]`?

The answer is - something catastrophic. Let's see why.

Let's assume the array elements are stored just like before - 

    mov     DWORD PTR [rbp-28], 1
    mov     DWORD PTR [rbp-24], 2
    mov     DWORD PTR [rbp-20], 3

But in our second file, we are doing `arr[1]`. So it will do something like

    mov     rax, QWORD PTR [rbp-28]
    mov     eax, DWORD PTR [rax+4]
    mov     DWORD PTR [rbp-16], eax

Can you see the problem? We are reading the content at `rbp-28`, but the content is 1, the first element of the array. So, essentially we are reading the content of memory address `1+4=5` which is an invalid location!

**Bottom line: Don't mix and match.**

Another difference is that a pointer name is a variable, but an array name is not. So you can do `pa++` and `pa=arr` but you cannot do `arr=pa` and `arr++`

But, there is a case where arrays and pointers are the same. That is in function parameters - 

    void f(int *pa, int arr[]) {
        int a = pa[1];
        int b = arr[1];
    }

What is the difference between `arr` and `pa`? There is no difference

    int a = pa[1]
    mov     rax, QWORD PTR [rbp-24]
    mov     eax, DWORD PTR [rax+4]
    mov     DWORD PTR [rbp-4], eax
    
    int b = arr[1]
    mov     rax, QWORD PTR [rbp-32]
    mov     eax, DWORD PTR [rax+4]
    mov     DWORD PTR [rbp-8], eax

The compiler treats `arr` and `pa` both as pointers, and that's about the only case you can be certain that using pointer in place of array works.

Technically, this is an illustration of an array-like syntax being used to declare pointers, rather than an example of pointers and arrays being the same.

Since pointers are like any other variable, you can have a pointer to pointers too.

    int **pa;

Here `pa` is a pointer to pointer to `int`. So, `*pa` will give you a pointer to `int`, and finally `**pa` will give you an `int`

    int a;
    int *pa = &a; // pointer to int
    int **ppa = &pa; // pointer to pointer to int

You can have pointers to array too. But before that, remember `[]` has higher precedence than `*`

    int (*pa)[3]; // pointer to array of 3 elements
    int *pa[3]; // 3 element array of pointer to int

What's the difference between pointer to an array and normal pointer? Consider

    int arr[3] = {1, 2, 3};
    int (*pa)[3] = &arr;
    int *pb = arr;
    
    printf("arr = %p\n", arr);
    printf("pa = %p\n", pa);
    printf("pb = %p\n", pb);

This prints

    arr = 0x7fff2632cc84
    pa = 0x7fff2632cc84
    pb = 0x7fff2632cc84

So, essentially they all point to the same location. And we already know `arr` is the same as a pointer to the first element. Now we see that `&arr` also contains the location of the first element.

Although `pa` and `pb` point to the same location, what they point to is very different. `pb` is a pointer to [int] so it points to a [int] which is the first element of `arr` whereas `pa` is a pointer to [array of 3 elements] so it points to an [array of 3 elements] i. e. the whole `arr`.

This is evident when you try to do arithmetic - 

    printf("pa + 1 = %p\n", pa + 1);
    printf("pb + 1 = %p\n", pb + 1);

This prints

    pa + 1 = 0x7fff2632cc90
    pb + 1 = 0x7fff2632cc88

`pb` is a pointer to `int`. So `pb+1` points to the next `int` 4 bytes after. Whereas `pa` is a pointer to array of 3 `int`. So `pa+1` will point to the next array of 3 `int` which is `3 * 4 = 12` bytes after, and indeed, `pa+1` is 12 bytes after `pa`. ( 0x7fff2632cc90 - 0x7fff2632cc84 = 12, these are in hexadecimal in case you're confused).

You can use a pointer to array just like a normal variable. Just remember the precedence - 

    int arr[3] = {1, 2, 3};
    int (*pa)[3] = &arr;
    
    int a = *pa[1]; // Wrong
    int b = (*pa)[1]; // Correct

The easiest way to remember is "Declaration follows usage." So the usage of a pointer will look like the way it was defined. Since we defined `pa` as `(*pa)[]`, its usage will also look the same.

One common mistake that students do, with the fact that arrays decay down to pointers in function parameters is working with multidimensional arrays.

If you have something like

    int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
    f(arr);

you might think since array names decay to pointers in function parameter, an array of array should decay to a pointer to pointer. So you might write the declaration of `f` as

    void f(int **m) {
     	...
    }

Unfortunately, this is wrong and will give a warning (but will compile)

    main.c:22:8: warning: passing argument 1 of ‘f’ from incompatible pointer type [-Wincompatible-pointer-types]                                                   
    main.c:11:5: note: expected ‘int **’ but argument is of type ‘int (*)[4]’

What happened here? It's easy.

If an array of [int] decays down to a pointer to [int], what should an array of [array of int] decay down to? Of course a pointer to [array of int]. Remember that the size is also part of arrays type. So, in our case, `arr` is an array of [4 element array of int]. So, it decays down to pointer to [4 element array of int].

So you should write

    void f(int (*m)[4]) {
    	
    }

Or, you can just take an array of array

    void f(int m[][4]){
    }

Note that only the size of the rightmost column is required in the formal parameters list.

### Pointers and Structures and Unions

Now we move on to `struct` and `union`. We can have pointers to them too.

    struct some_struct {
    	int p;
        int q;
    }
    
    struct some_struct a;
    struct some_struct *pa = &a;

Or if you prefer a `typedef`

    typedef struct some_struct {
    	int p;
        int q;
    } some_struct;
    
    some_struct a;
    some_struct *pa = &a;

An interesting situation occurs when you want to access members of `struct` using pointer. Suppose you want to access the member `p` through `pa`. You might do

    int k = *pa.p;

Except, this doesn't do what you expect. The operator `.` has higher precedence than `*` so `*pa.p` is same as `*(pa.p)`. So instead of dereferencing `pa` and then accessing the member `p`, you end up accessing the member `p` and then dereferencing it. But `pa` doesn't have a member `p`. So, it gives a compiler error.

Instead, you want to write this

    int k = (*pa).p;

Which works the way you want. But writing this is tedious, and turns out that we write this so much that they have a special operator `->`

    int k = pa -> p;

`pa -> p` is same as `(*pa).p` but looks neat and clean.

The case of unions is a little bit involved. Quoting cppreference - 

> A pointer to a union can be cast to a pointer to each of its members (if a union has bit field members, the pointer to a union can be cast to the pointer to the bit field's underlying type). Likewise, a pointer to any member of a union can be cast to a pointer to the enclosing union.

What it means is that, if you have a pointer to a union, you can cast it to any of its members, and vice versa. Take a look

    typedef union some_union {
            int p;
            char q;
        } some_union;
        
    some_union a = {1}; // Initialize a with p = 1
    some_union *pa = &a;
    printf("%d\n", pa -> p); // Access p through pointer to a
    
    int * pb = (int *) pa; // cast pa to point to p directly
    printf("%d\n", *pb);

This prints

    1
    1

Here, I could cast the pointer to `a` to an `int*` and it automatically pointed to the member `p`. Similarly, if I had cast it to `char*` it would point to `q`.

Conversely, if I had a pointer to `p`, I could cast it to a pointer to `some_union` and it would point to `a`

    int *pc = &(a.p);
    some_union *pd = (some_union *)pc;
    a.q = 'a';
    printf("%c\n", pd -> q);

This prints `a` as expected.

## Ministry of Pointer Magic

### Pointers and Function

It is possible to have pointers to functions too. Remember that the **return type, the number of parameters it takes, and the type of each parameter** - these 3 are parts of the type of a function. Hence you must provide these during pointer declaration. Also worth noting `()` has higher precedence than `*`

    int *f(); // a function that returns a pointer to int
    int (*f)(); // a pointer to a function that takes no argument and returns an int
    int (*f)(int); // a pointer to a function that takes an int and returns an int

A pointer to function can be used just like other pointers - 

    int f(int a) {
        return a+1;
    }
    
    int main()
    {
        int (*fp)(int) = &f;
        printf("%d\n", (*fp)(1));
    	// printf("%d\n", *fp(1)); Wrong~ Won't cpmpile
        return 0;
    }

This prints `2` as you'd expect.

Remember I talked about declaration follows usage? Well, turns out that in the case of pointer to functions, that rule can be ignored. For example, this works

    printf("%d\n", (**fp)(1));

And so does this

    printf("%d\n", (*********fp)(1));

And weirdly enough, this too

    printf("%d\n", fp(1));

So, in the case of functions, **not only you can dereference as many times as you want, you can drop the dereferencing altogether and just use the pointer as if it were a function itself**. Another one of those C quirks.

Finally, you can get wild with pointers and arrays and function like

    char *(*(*foo)[5])(int); // foo is a pointer to array of 5 elements of pointer to a function that takes an int and returns a pointer to char
    int *(*foo)(int *, int (*[4])()); // foo is a pointer to function (that takes a pointer to int and a 4 element array of pointer to functions that return int) and returns a pointer to int

You get the idea. Yes, it can get pretty messy, but once you know the syntax, and you have [cdecl](__GHOST_URL__/p/7a2aaf27-dbd2-4822-8dd4-3bbb6da203ac/cdecl.org), you can easily breeze through them (or read my [article](__GHOST_URL__/declaration-in-c-p1/)) 

As for how you can use a function pointer. here's an example of a simple calculator

    #include <stdio.h>
    
    float add(float a, float b) { return a+b; }
    float sub(float a, float b) { return a-b; }
    float mul(float a, float b) { return a+b; }
    float divide(float a, float b) { return a/b; }
    
    
    int main()
    {
        float (*arr[4])(float, float) = { add, sub, mul, divide };
        int n;
        float a, b;
        printf("Enter two numbers: ");
        scanf("%f%f", &a, &b);
        printf("Enter 1 for addition, 2 for subtraction, 3 for multiplication, 4 for division: ");
        scanf("%d", &n);
        printf("%f", arr[n-1](a, b));
    
        return 0;
    }

We are storing all 4 operations in an array and when the user enters a number, we call the corresponding operation.

### Qualified types

Each type in C can be qualified by using qualifiers. In particular we have 3 - `const`, `volatile`, and `restrict`. Here we will look at `const` and `restrict`.

Adding `const` to a type effectively marks it read-only, so that attempting to change the value will result in a compiler error.

    const int p = 1;
    p = 2; // error: assignment of read-only variable ‘p’

Turns out, `int const` and `const int` both are valid. Now if I throw pointers into the party, I get some fun stuff

    const int *p;
    int * const p;
    const int * const p;

Can you guess which one is what?

To untangle this, we will remember `some_data_type *p` declares `p` to be a pointer to `some_data_type`

Hence, `const int *p` can be thought of as `(const int) *p`. So that `p` is a pointer to a `const int`. It means, whatever `p` is pointing to is a `const int` and you cannot change that. However, `p` itself is not `const` and can be changed.

    const int a = 1;
    const int b = 2;
    const int *p = &a;
    
    p = &b; // works. You can change p
    // *p = 3; error: assignment of read-only location ‘*p’

For the second one compare it with `int const p` which declares `p` as a read only `int`. So, `int * const p` should declare `p` as read-only `int*`. This means the pointer itself is `const` and you can't change `p` , but you can change what `p` is pointing to.

    int a = 1, b = 2;
    int * const p = &a;
    
    *p = 2; // Works. you can change *p
    // p = &b; error: assignment of read-only location ‘p’

And finally, `const int * const p` declares that both `p` and `*p` are read-only. So you can neither change `p` nor `*p`.

    const int a = 1, b = 2;
    const int * const p = &a;
    // *p = 2; error: assignment of read-only location ‘*p’
    // p = &b; error: assignment of read-only location ‘p’

Now let's look at the `restrict` keyword. The `restrict` keyword, when applied to a pointer `p`, tells the compiler that as long as `p` is in scope, only `p` and pointers directly or indirectly derived from it (e. g. `p+1` ) will access the thing it's pointing to.

Confused? Let's see an example.

    void f(int *p, int *q, int *v) {
        *p += *v;
        *q += *v;
    }

Here is the assembly generated after enabling optimization -

    mov     eax, DWORD PTR [rdx]
    add     DWORD PTR [rdi], eax
    mov     eax, DWORD PTR [rdx]
    add     DWORD PTR [rsi], eax
    ret

The problem in this function is, `p`, `q` and `v` might point to the same location. So that when you do `*p += *v`, it might happen that `*v` also gets changed because `p` and `v` were pointing to the same location. 

This is why `*v` is first loaded into `eax` by `mov     eax, DWORD PTR [rdx]`. Then it is added to `*p`. Again, we have to load `*v` because at this point, we are not sure if `*v` has changed or not.

Now if I update the function as follows -

    void g(int *restrict p, int *restrict q, int *restrict v) {
        *p += *v;
        *q += *v;
    }

the compiler is free to assume that `p`, `q` and `v` all point to different locations, and can load `*v` only once, and indeed it does

    mov     eax, DWORD PTR [rdx]
    add     DWORD PTR [rdi], eax
    add     DWORD PTR [rsi], eax

Note that it is up to the programmer to guarantee that the pointers do not overlap. In case they do, it is undefined behavior.

You can read more about `restrict`[here](https://en.cppreference.com/w/c/language/restrict).

That's probably enough for one post. To quickly recap, you have learned - 

1. What pointers are.
2. How to declare and dereference pointers.
3. Pointer arithmetic.
4. Pointer comparison.
5. Pointer to array and array of pointers.
6. Pointers and arrays are not the same.
7. Pointer arithmetic and array indexing are equivalent.
8. Array in function parameter decay to a pointer.
9. Pointers to a multidimensional array.
10. Pointers to structures and unions.
11. Pointer to functions.
12. Pointer to wild exotic types.
13. `const` and `restrict` with pointers.

Note that most of these hold true in C++ also, although you have minor changes, and some new stuff like smart pointers.

Hopefully, you learned something new in this post. Subscribe to [my blog](https://blog.abhattacharyea.dev) for more.