* 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.
Using 0 as a special error value is a kind of a hack though. What you're really doing is returning "either an error or an index", which is a different type than "an index". Empirically, using one language type for two concepts like this, allowing errors to be silently dropped as values can be implicitly converted from one to the other, has been very error prone. See the discussions about nullable pointers for example.
WRT strings with metadata, if you construct your data structure so that the length and other metadata are at negative offsets from the pointer value that gets passed around, then the array data can be at offset zero again, so it's even more efficient. So zero still wins :).
But the arguments for 0-based indexing are also deeper than C arrays. C arrays are just one place where it's clearly visible.
The Pascal approach unfortunately doesn't solve the problem either. "Everyone can do what they like" is fine as long as no one ever has to read or interface with code written by other people.
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.