I'd be surprised if the use of zero vs one actually made any difference, from a number of operations point of view -- from the compiler's point of view, the first element in the array is the first element in the array, no matter what we call it.
I mean in an extreme edge case maybe if you are computing an index, and it happens to be zero, then in your math maybe some identity related to zero could be exploited (go to element simple_integer*complicated_function(), where simple_number might be zero) but that seems a bit silly.
In x86 you could probably hide it in the addressing but that does not mean it does not to be computed
1. Optimizing compilers exist.
2. Addressing at fixed offsets is cheap (a single instruction): https://en.wikipedia.org/wiki/Addressing_mode
0-based indexing is superior (as a default) because it simplifies a lot of common math, as discussed in TFA.
4. Macro expansion is often used to store indexes and other 'magic numbers' in Assembler, and this pattern originated either when Assembly was the primary programming language, or even earlier when humans directly punched out cards with machine instructions. Compilers in any remote sense of the luxury we have today did not exist or were not common.
char at(char matrix[10][10], char i, char j) {
return matrix[i][j];
}
still has more computation on 1-indexed than on 0-indexed, I believe.memory_address + array_width*i + j
or
memory_address + array_width*(i-1) + (j-1) = (memory_address-array_width-1)+ array_width*i + j
so you can just absorb the extra computation into the pointer.
If that was true, Fortran, Julia, R and Matlab would use 0 instead of 1-based.