# What's in a name?

In C, pointers and arrays are closely related concepts. In fact, arrays in C are essentially a contiguous block of memory, and the name of the array often decays into a pointer to the first element. This is why you can use pointer notation to access array elements.

Let's go through an example with well-commented code:

```c
#include <stdio.h>

int main() {
    // Declare an array of integers
    int myArray[5] = {10, 20, 30, 40, 50};

    // Declare a pointer to an integer and initialize it with the address of the first element of the array
    int *ptr = myArray;

    // Access array elements using array notation
    printf("Using array notation:\n");
    for (int i = 0; i < 5; ++i) {
        printf("myArray[%d] = %d\n", i, myArray[i]);
    }

    // Access array elements using pointer notation with bracket syntax
    printf("\nUsing pointer notation:\n");
    for (int i = 0; i < 5; ++i) {
        // The following two lines are equivalent
        printf("*(ptr + %d) = %d\n", i, *(ptr + i));
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    return 0;
}
```

In this example:

1. We declare an array `myArray` of integers with five elements.
    
2. We declare a pointer to an integer `ptr` and initialize it with the address of the first element of `myArray`.
    
3. We use a loop to print the elements of `myArray` using array notation (`myArray[i]`).
    
4. We use another loop to print the elements of `myArray` using pointer notation with bracket syntax (`ptr[i]` is equivalent to `*(ptr + i)`).
    

In C, `ptr[i]` is equivalent to `*(ptr + i)`, so you can use either notation to access array elements through a pointer. The bracket notation provides a convenient and readable way to work with pointers like arrays.

In C, a multidimensional array is essentially an array of arrays. The name of a multidimensional array, when used in an expression, represents the address of its first element. Let's go through an example with a two-dimensional array:

```c
#include <stdio.h>

int main() {
    // Declare a 2D array of integers
    int myArray[3][4] = {
        {10, 20, 30, 40},
        {50, 60, 70, 80},
        {90, 100, 110, 120}
    };

    // Declare a pointer to an array of integers and initialize it with the address of the first row of the 2D array
    int (*ptr)[4] = myArray; // pointer to an array of 4 integers

    // Access 2D array elements using array notation
    printf("Using array notation:\n");
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 4; ++j) {
            printf("myArray[%d][%d] = %d\n", i, j, myArray[i][j]);
        }
    }

    // Access 2D array elements using pointer notation with bracket syntax
    printf("\nUsing pointer notation:\n");
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 4; ++j) {
            // The following two lines are equivalent
            printf("ptr[%d][%d] = %d\n", i, j, ptr[i][j]);
            printf("*(*(ptr + %d) + %d) = %d\n", i, j, *(*(ptr + i) + j));
        }
    }

    return 0;
}
```

The expression `ptr[i][j]` can be equivalently expressed using pointer arithmetic as `*(*(ptr + i) + j)`. Let's break down the equivalence:

1. `ptr[i]`: This expression represents the ith row of the 2D array. It's equivalent to `*(ptr + i)` because the name of the array `ptr` by itself is a pointer to the first row, and adding `i` moves the pointer to the ith row.
    
2. `ptr[i][j]`: This expression further accesses the jth element in the ith row of the 2D array. It's equivalent to `*(*(ptr + i) + j)` because `*(ptr + i)` gives the address of the ith row, and adding `j` moves to the jth element in that row.
    

So, in summary, `ptr[i][j]` is equivalent to `*(*(ptr + i) + j)` in terms of pointer arithmetic.

`ptr` is not a double-pointer. It is a pointer to an array of integers (specifically, an array of 4 integers). The type of `ptr` is `int (*)[4]`.

A double-pointer in C would be a pointer that points to another pointer. In the context of a 2D array, a double pointer could be used to point to an array of pointers, where each pointer points to a row of the 2D array. Here's an example:

```c
#include <stdio.h>

int main() {
    // Declare a 2D array of integers
    int myArray[3][4] = {
        {10, 20, 30, 40},
        {50, 60, 70, 80},
        {90, 100, 110, 120}
    };

    // Declare a double pointer to int and initialize it with the address of the first row of the 2D array
    int **ptr = (int **)myArray; // casting to int**

    // Access 2D array elements using double pointer notation
    printf("Using double pointer notation:\n");
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 4; ++j) {
            printf("ptr[%d][%d] = %d\n", i, j, ptr[i][j]);
        }
    }

    return 0;
}
```

In this example, `ptr` is a double-pointer (`int **`) because it points to an array of pointers, and each of those pointers, in turn, points to a row of the 2D array.
