The reason for the convention is that it used to be just a pointer (adress) to consecutive elements in memory. A[x] is then literally translated to the adress of A + sizeof(x)*x. Meaning that the first element is at A[0].
There's a syntax for indexing starting from 0, it's
*(&arr+0) to *(&arr+(n-1))
For the rest of us who are manipulating sets of values and not offsets on pointers and aren't delusionally attached to conventions, there's arr[1] to arr[n]
Addition is commutative so of course array indexing is and why the hell are you taking the address of a pointer. Also it's not "int pointer foo" but "foo, dereferenced, is an int" that's why it's int *foo not int* foo. I won't die on that mountain fortress because it is unassailable. Never write char **argv (but char *argv[]) but it's vital to understand why it doesn't make a difference to the compiler. It's what passes as self-documenting code in C land.
Also 0-based indexing is older than C. It's older than assembly.