The first thing that trips up almost every programming beginner is the question of why the first element is number 0. Not being number 1 just feels wrong. I'll admit I initially assumed it was somebody's practical joke.
It certainly runs against everyday intuition. In the world of computers, however, counting from 0 has some powerful advantages, both mathematical and structural. I want to unpack the reason from two angles: memory addresses and the elegance of calculation. For a practical joke, it turns out to be surprisingly well made.
The Index as a Distance
The most fundamental reason is that an index expresses not "which number" but "how far from the start." An array, to begin with, is something like a row of boxes laid out contiguously in memory. You could just as well say: how many steps from the first box. When a computer looks up the location of a particular element, it performs the following calculation behind the scenes.
element address = start address + (index × element size)
If arrays started at 1, the calculation would look like this every time.
element address = start address + ((index - 1) × element size)
In other words, you would have to dutifully subtract 1 on every single access. Because the spot you haven't moved from is called "number 1," every lookup needs a correction. For the computer that is pure waste, whereas starting at 0 leaves a plain addition: "start address + distance." Index 0 simply means zero steps from the start.
Ranges That Write Themselves
Next, ranges become cleaner. For a 0-based array of lengthn, the indexes run 0 <= i < n. A range such as a slice can be written as [start, end). That is the half-open interval, which includes start and excludes end.
Set things up this way and the number of elements falls out of a simple subtraction: end - start. You are just counting the steps from start to end. With 1-based indexing, on the other hand, the range becomes 1 <= i <= n. The count is off unless you add + 1 after subtracting.
When writing algorithms, the presence or absence of that "plus 1" is a breeding ground for off-by-one errors. Those who shrug it off as "only 1" are the first to trip at the edge of a loop. In that sense, too, 0-based indexing is the safer choice.
The Inheritance from C
Finally, there is history. Many modern languages, including Java, Python, and JavaScript, are 0-based. That is because they inherit heavily from the design of C.
In C, the array subscripta[i] is merely a rewriting of the pointer arithmetic *(a + i). Syntactic sugar, in other words. The value added to a pointer literally meant the offset from the start, the number of steps. Given that, 0-based indexing was inevitable.
And the design was simply too rational to ignore. The languages that came after followed it, and it quietly became the de facto standard. Far from a practical joke, it was a perfectly respectable convention all along.
So that's why array indexes start at 0, seen from three angles: the index as a distance, range arithmetic, and the inheritance from C. Counting from 1 is natural for humans, but computing an offset from 0 is natural for computers.array[0] is nothing more than the spot zero steps from the start. A different way of counting is simply a different point of view.