The reason is that, for an array (or vector), you find the memory position for the i-th element with the base address + i*word_length. And the first element is in the base address - so has index 0.
It has memory offset 0, which we use as the array index for convenience so that there's no distinction between a memory offset-base and the corresponding array index-base. That's what happens when your arrays are barely different from pointers, as in C. If your arrays aren't just a stand-in for raw pointers, then there's little reason to require 0-based indexing. You can use more natural indexes based on your particular application, and many languages do allow arbitrary indices.