Речь идет об удобстве. Конечно, память на самом деле все последовательная, но иногда хочется иметь доступ к вещам с двумя индексами (например, реализуя матрицы).
Рассмотрим массив 3х3. Удобно думать о памяти так:
----------------------------
| [0][0] | [0][1] | [0][2] |
|--------------------------|
| [1][0] | [1][1] | [1][2] |
|--------------------------|
| [2][0] | [2][1] | [2][2] |
----------------------------
Но в памяти это, конечно, действительно выглядит так:
----------------------------------------------------------------------------------
| [0][0] | [0][1] | [0][2] | [1][0] | [1][1] | [1][2] | [2][0] | [2][1] | [2][2] |
----------------------------------------------------------------------------------
Мы просто разбили его на строки, чтобы мы могли легко понять его как двумерный. Мы обращаемся к нему с двумя параметрами, потому что хотим, потому что это удобно для нашего кода. Язык обеспечивает эту реализацию, которая позволяет получить доступ через два индекса, даже если под прикрытием он линейный и может быть доступен с одним индексом.
Эта картинка также должна помочь вам понять, почему ее можно считать массивом массивов. Вот слегка измененная картинка для акцента:
|||--------------------------|||--------------------------|||--------------------------|||
||| [0][0] | [0][1] | [0][2] ||| [1][0] | [1][1] | [1][2] ||| [2][0] | [2][1] | [2][2] |||
|||--------------------------|||--------------------------|||--------------------------|||
Как видите, там действительно три одномерных массива. Итак, когда вы пишете array[1]
, вы ссылаетесь на второй одномерный компонент полного двумерного массива, то есть на вторую группу из трех в памяти. При добавлении второго индекса, array[1][2]
берет третий элемент этого одномерного массива, приводя вас к нужному элементу двумерного массива.