* Zero is the first unsigned integer. If you start at 1, you're wasting one
Counter-argument: If you start indexing at 1, it allows you to encode an exceptional condition (e.g., element of a search not found) as 0 when the result is an unsigned integer. * p[0] == *p, and it makes no difference whether pointer arithmetic had been invented, because pointers are just memory addresses, and that's what the cpu works with
Counter-argument: This only holds for fixed-length arrays, strings, and the C/C++ idiosyncrasy that conflates pointers with arrays (at the expense of runtime safety). If you have variable length arrays or strings, then the beginning of the actual array/string contents will be at an offset (allowing for length/capacity/etc. fields). In fact, the offset may be exactly one (if you have a length field the size of each element), making one-based indexing the more efficient choice.You can make up ANY number of arguments for zero- or one-based indexing, and it will depend entirely on how much weight you assign to each of them.
Or, alternatively, you can use the Pascal approach, where you can specify the beginning index on a per-array basis.