Почему индекс начинается с нуля
Перейти к содержимому

Почему индекс начинается с нуля

  • автор:

Как сделать, чтобы массив начинался не с 0, а 1 индекса?

Если программируете на С++, то можно переписать действие оператора [ ], но я вам не рекомендую это делать, можете непосредственно вычитать единицу при индексации:

Отслеживать
ответ дан 7 сен 2014 в 17:27
326 1 1 серебряный знак 5 5 бронзовых знаков

Свою функцию написать по работе с массивом. В аргументе передаете 1 для обозначения 1-го элемента и внутри хитро меняете на 0, чтобы никто не видел. А в стандартном исполнении никак. Например, в Си имя массива есть указатель на первый элемент, а индекс ноль просто задает смещение относительно начала массива. Нулевое смещение и дает ссылку на первый элемент. Все привязано к технике. А так только свою функцию реализовывать. Ноль слишком раздражает, ну представьте, что это единица, просто пухлая)

Отслеживать
11 1 1 золотой знак 2 2 серебряных знака 8 8 бронзовых знаков
ответ дан 5 сен 2014 в 14:26
aleksandr_mai aleksandr_mai
231 1 1 серебряный знак 6 6 бронзовых знаков

Ну, если с элементами массива работать через указатель, то не просто, а очень просто int a[10], *my_array = a — 1; int i; for (i = 1; i < 11; i++) my_array[i] = i * 2; // проверим for (i = 0; i < 10; i++) printf ("%d : %d (%d : %d)\n", i + 1, my_array[i + 1], i, a[i]); (тщательней изучайте матчасть) -- @Akbar, откровенно говоря, идея индексировать с 1 -- дурацкая. Мало того, что окружающие Вас будут плохо понимать, но еще и сами будете на самом деле делать больше ошибок, чем при индексировании с 0.

Почему индекс массива начинается с нуля?

Причина 1:
Рассмотрим int arr [100]. Ответ заключается в том, как компилятор интерпретирует arr [i] (0 arr [i] интерпретируется как * (arr + i). Теперь arr — это адрес массива или адрес 0-го элемента индекса массива. Таким образом, адрес следующего элемента в массиве — arr + 1 (поскольку элементы в массиве хранятся в последовательных ячейках памяти), дальнейший адрес следующего элемента — arr + 2 и так далее. Исходя из приведенных выше аргументов, arr + i означает адрес на расстоянии i от начального элемента массива. Следовательно, следуя этому определению, i будет нулевым для начального элемента массива, потому что начальный элемент находится на расстоянии 0 от начального элемента массива. Чтобы соответствовать этому определению arr [i], индексирование массива начинается с 0.

CPP

using namespace std;
// Below two statements mean same thing
cout << *(arr + 1) << " " ; cout << arr[1] << " " ; Выход:

Напрашивается вывод, нам нужен произвольный доступ в массиве. Чтобы обеспечить произвольный доступ, компиляторы используют арифметику указателей для достижения i-го элемента.

Причина 2:

Современные языки, особенно C ++, используют строковый мажор упорядочивание хранения двумерных массивов.

  1. индексы массива, начиная с 1
  2. индексы массива, начиная с 0

case 1 ( array indices start from 1 ) : &( arr[i][j] ) = address + [ ( i-1 )*n + ( j-1 ) ]*( sizeof(int) ) ] so here we are performing 6 operations case 2 ( array indices start from 0 ) : &( arr[i][j] ) = address + [ ( i )*n + ( j ) ]*( sizeof(int) ) ] and here we are performing only 4 operations

Итак, мы видим, что мы выполняем на 2 операции меньше, когда храним 2D-массивы и получаем адрес элемента. Похоже, это не имеет смысла, но это так! При работе с данными большого размера это может улучшить производительность и скорость. случай 1 может показаться удобным для пользователя, но случай 2 более эффективен. Вот почему большинство языков, таких как C ++, PYTHON, JAVA, используют массивы, начинающиеся с индекса 0, и редко языки, такие как массивы Lua, начинающиеся с индекса 1.

Хотите узнать о лучших видео и практических задачах, ознакомьтесь с базовым курсом C ++ для базового и продвинутого уровня C ++ и курсом C ++ STL для базового уровня плюс STL. Чтобы завершить подготовку от изучения языка к DS Algo и многому другому, см. Полный курс подготовки к собеседованию .

Почему массивы начинаются с нуля

Самое очевидное объяснение: индекс — это смещение относительно начала массива. Так элементы массива легче адресовать в памяти.

Проверим это на C.

#include int main() < int data[3] = ; int i = 0; printf("Array address: %p\n", data); do < printf("Array[%u] = %p\n", i, (void *)(&data[i])); i++; >while(i

Array address: 0x7ffd7c514a6c
Array[0] = 0x7ffd7c514a6c
Array[1] = 0x7ffd7c514a70
Array[2] = 0x7ffd7c514a74

Как первый (нулевой) элемент, так и сам массив находятся по одному и тому же адресу, поскольку 0-й элемент удалён на 0 элементов от начала. Эта связь между указателями и массивами в C настолько тесная, что их даже можно рассматривать вместе.

Однако это ответ на вопрос «зачем», а не «почему». Нумеровать массивы с нуля стали не сразу. Удивительно, но развитие такого простого вопроса не умещается в предложении или абзаце.

Потому что так удобнее

К началу 1960-х годов сформировалось три подхода к организации структуры, которую сегодня мы называем статическим массивом:

    Исчисление с 0. Нижняя граница массива начинается с нуля.

Непривычно для обывателя, если это не житель Германии, свыкшийся с нумерацией этажей в зданиях. Последний элемент массива из, скажем, 8 элементов имеет номер 7.

Не всё так однозначно. Единообразия даже у самых первых языков программирования не существовало:

  1. Нумерация с нуля: LISP 1.5, APL (допускает выбор при запуске программы).
  2. Нумерация с единицы: APL, Бейсик, ранний Фортран.
  3. Произвольные границы: Алгол-60, затем Фортран, CPL, SIMULA, CLU, PL/1, Кобол, Паскаль, Алгол-68, JOVIAL.

Конечно, это не самый сильный аргумент. Часто языки снабжались синтаксическим сахаром для нумерации с 1. К примеру, в Алголе массив V[0:3] начинается с 0, а массив V[3] — с 1.

Создатель языка APL Кеннет Айверсон в книге «A Programming Language» объясняет, почему далее по тексту он будет пользоваться нумерацией элементов массива с нуля. При этом язык допускает отсчёт как с единицы, так и с нуля. Айверсон приводит уже описанный выше аргумент о сдвигах в позиционной нумерации.

Тем не менее и в эпоху до C звучали предложения выбирать нумерацию с 0. Логично предположить, что это была историческая неизбежность, до которой оставалось несколько лет.

Потому что парусные регаты мешали вычислениям

В 1967 году Мартин Ричардс впервые реализует компилятор своего детища — BCPL (Basic Combined Programming Language). Этот язык программирования был призван исправить проблемы созданного в начале шестидесятых языка CPL путём отказа от технологий, затрудняющих компиляцию.

Первый компилятор BCPL был написан для операционной системы CTSS машины IBM 7094 Массачусетского технологического института. На тот момент компьютеры — это уже не целые комнаты и электронные лампы, но всё ещё огромные шкафы с транзисторами и консоли управления без экранов. Ресурсов серии 7090 хватило, чтобы запустить американца в космос. Но мощность измерялась в тысячах машинных слов, микросекундах тактов и килофлопсах, а цена — в миллионах долларов.

В пятидесятых и шестидесятых IBM выдавала институту щедрые скидки на свои научные компьютеры или даже предоставляла их бесплатно. В 1963 году в МТИ поставили IBM 7094. На компьютер уговор был такой: 8 часов в сутки получают специалисты института, 8 часов — другие колледжи и университеты Новой Англии, а третью смену отдавали самой IBM для личных нужд.

На этом особенности не кончались. Несколько раз в год IBM обсчитывала регаты: президент IBM соревновался в заплыве на больших яхтах в проливе Лонг-Айленд, и каждое из судов получало очки гандикапа по специальной сложной формуле. Когда в вычислительный центр приходило задание, операторам полагалось всё бросить и запустить вычисления этих очков.

Машинный зал с установленным компьютером IBM 7094 в Колумбийском университете США. Фотоархив

Вообще, и без этого был шанс не получить результат вычислений. Изначально 7094 работал в режиме пакетной обработки. Вспомогательная система на IBM 1401 загружала пакет задач с перфокарт на ленту, а затем запускала задачи одну за другой и записывала результаты для последующей печати. Для каждой задачи приводилась оценка времени. Если задача выходила за пределы отпущенного, её принудительно прерывали.

В итоге компиляция в BCPL была оптимизирована по максимуму. То есть и указатели на элементы массива должны максимально близко походить на машинный код, без необходимости вычитать единицу при указании на элемент массива. Хотя во время выполнения программы не играет роли схема организации массивов, нумерация с нуля могла появиться для оптимизации времени компиляции.

Впрочем, конкретно эта версия — лишь гипотеза Майка Хойе. Ей нет никаких подтверждений от собствено Ричардса или хотя бы упоминаний в литературе. Мартин Ричардс в переписке с Хойе лишь приводит общие соображения о том, что он хотел достичь близости к машинному коду, поэтому указатель p и p + 0 — это одна и та же переменная. Ни на какие яхты Ричардс не жалуется.

К тому же на момент появления первого компилятора BCPL уже была готова операционка Compatible Time-Sharing System. В ней на одной машине с разделением времени компьютер выполняет одну задачу за один раз, но с оптимизацией ввода и вывода, чтобы паузы одного пользователя заполнялись работой других. Это уже не былая пакетная обработка задач.

Точно известно, что в дальнейшем BCPL значительно повлиял на все современные языки программирования.

В 1969 году Кен Томпсон урезал функциональность BCPL до языка B. В дальнейшем, для развития операционной системы Unix, Деннис Ритчи улучшил B добавлением функций PDP-11, в результате чего и получился C. Полвека спустя список языков, на которые оказал влияние C, занимает в «Википедии» целую страницу.

В языке BCPL v!5 и 5!v совпадают, поскольку являются указателем на !(v+5) или !(5+v) . Аналогично в C v[5] эквивалентно 5[v] .

Потому что так предложил Дейкстра

В 1982 году Эдсгер Дейкстра опубликовал статью «Почему нумерация должна начинаться с нуля». В ней он низверг как нумерацию с единицы, так и произвольные границы индексов. Очевидно, что Дейкстра — не человек, который легко поддаётся влиянию C, но также он раскритиковал Алгол-60, своё собственное детище, и Паскаль.

Известно, что Дейкстра ближе к концу жизни всё больше писал от руки, а не набирал тексты на машинке. Архив рукописей Эдсгера Вибе Дейкстры

Если необходимо записать интервал натуральных чисел 2, 3, …, 12 без опухоли из точек, возможны четыре варианта:

Затем автор замечает, что для нижней границы предпочтителен знак «⩽», поскольку наименьшее натуральное число существует. В противном случае, если последовательность начинается с наименьшего натурального числа, нижняя граница не будет натуральным числом.

Затем статья делает вывод, что из соображений простоты для последовательности из N членов предпочтительнее диапазон 0 ⩽ i < N , а не 1 ⩽ i < N+1 .

Куда менее известный документ — это полушуточная техническая заметка от 1 апреля 1980 года IEN 137 под названием «О священных войнах и призыв к миру» [On Holy Wars and a Plea for Peace].

В заметке Дэнни Коэн приводит интересный аргумент: в системе счисления с основанием b при отсчёте с нуля первые b ^ N неотрицательных чисел представляются ровно N цифрами. Например, если речь про двоичную запись, то 2 ^ 3 = 8 , и восьмой элемент массива будет иметь номер 1112, а не 10002. Понятно, что с такими преобразованиями легко бы мог справиться компилятор.

Потому что так более элегантно

Это объяснение может раздражать субъективностью. Соображения о красоте у каждого свои и совпадать не обязаны. Но авторы языков программирования — тоже люди со своими предпочтениями.

В конце восьмидесятых Гвидо ван Россум при создании Python как учёл свой предыдущий опыт с языком ABC, так и задумал привлечь аудиторию хакеров от мира Unix и C.

С 1983 года Гвидо работал в Центре математики и информатики в Амстердаме над реализацией языка ABC. Проект ставил целью создать язык программирования, пригодный для обычных людей, но не настолько ужасно реализованный, как Бейсик. Нумерация массивов в ABC начиналась с единицы. Такой же схемы придерживались другие знакомые Россуму языки — Алгол, Фортран и Паскаль.

Однако в вышедшем в 1991 году Python нумерация начинается с нуля, а не единицы. Получилось так в результате долгих размышлений.

Одна из причин — слайсы. Чаще всего при создании слайса используются операции «получить первые n элементов» и «начиная с i, получить следующие n элементов». При этом первый случай эквивалентен i == первый индекс . Гвидо посчитал, что лучше, если обе операции возможны без лишней головной боли в виде коррекции +1 и −1.

Если первый элемент имеет номер 1, то возможно указывать первый элемент и число элементов, которые нужно получить. Россум уже был знаком с подобным по ABC и вполне мог бы положиться на этот опыт.

Тем не менее автора Python очаровал синтаксис слайсов полуоткрытого интервала, если нумерация начинается с нуля: a[:n] (или a[0:n] ) и a[i:i+n] . К примеру, строку a легко разбить на три части a[:i] , a[i:j] и a[j:] .

Заметим, что в воспоминаниях Гвидо не приводит ни призывы гениев информатики, ни практики других языков, ни принципы, заложенные мастодонтами компьютерных вычислений шестидесятых. Зато слово «элегантность» в пяти абзацах его объяснения встречается 3 раза.

Почему же массивы начинаются с нуля?

Так исторически сложилось.

Почему во многих языках индексирование начинается с 0?

Предположим, что у вас есть массив из целых чисел, каждое по четыре байта. Массив расположен в памяти последовательно. Чтобы получить элемент N, надо взят указатель на массив и прибавить к нему (N — 1) * 4 байт, или, если вести отсчет от нуля, просто N * 4 байта

14 авг 2016 в 18:12
@Etki: А почему не как ответ?
14 авг 2016 в 18:17
В электронике отсчет ножек многих микросхем тоже начинаются с 0. Я думал это связано.
14 авг 2016 в 18:17
На линейке первая цифра тоже 0! координата первого значения начинается с нулевой точки.
14 авг 2016 в 19:09

мне лень переводить, но большинство серьезных причин — приведено здесь quora.com/… И там ссылка на статью Дейкстры

14 авг 2016 в 19:38

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

В первую очередь потому, что с точки зрения реализации понятие «индекса» элемента в непрерывном агрегате напрямую связано с понятием «смещения» элемента от начала агрегата в памяти. Понятно, что смещение самого первого элемента агрегата равно нулю. Соответственно и индекс его разумно взять равным нулю.

Другими словами, как ни верти, но индекс первого элемента в агрегате все равно придется пересчитывать в значение 0 на машинном уровне. В такой ситуации реализация индексации с ненулевой базой будет всегда волей-неволей подразумевать неявное приведение ее к индексации с нулевой базой. В многих языках программирования просто не считают необходимым это делать. Зачем, действительно?

Отслеживать
ответ дан 14 авг 2016 в 23:03
AnT stands with Russia AnT stands with Russia
69.4k 3 3 золотых знака 65 65 серебряных знаков 140 140 бронзовых знаков

При выделении динамической памяти в C/C++ возвращаемый указатель не обязательно указывает на начало выделенного из кучи участка памяти.

14 авг 2016 в 23:09

@hunter: А какое это имеет значение в данном случае? Главное, чтобы указатель указывал на начало пользовательской области памяти, т.е. на ту точку, от которой будут отсчитываться смещения. А то, что блок может также содерждать какие-то служебные области со служебными данными к данному вопросу не относится никак. Что именно вы хотели сказать?

14 авг 2016 в 23:11

то и хочу сказать что даже при выделении не фрагментированной области памяти из указателя на нулевой элемент рано или поздно может возникнуть необходимость получить указатель на ячейку памяти с меньшим адресом.

14 авг 2016 в 23:17

@hunter: Прекрасно. Но какое это имеет отношение к теме индексации? «Ячейка с меньшим адресом» не является частью пользовательских данных, и доступаться к ней путем отрицательной индексации пользовательских данных никто не будет (в здравом уме). Такая область памяти будет прекрасно доступна путем вычета некоего известного платформе смещения из указателя «пользовательского» уровня и все. Я по прежнему не вижу, какое это все имеет отношение к рассматриваемой теме или к моему ответу.

14 авг 2016 в 23:22

@avp: До языка С были языки B и BCPL с той же самой нулевой индексацией. Язык С унаследовал свой подход к массивам именно из B и BCPL, с одним важным изменением. В B и BCPL массивы физически реализовались через обычные указатели. В С физические указатели убрали, но стали их «эмулировать» (пресловутый «array type decay»). А все остальное в С осталось, как в B и BCPL именно для облегчения обратной совместимости с этими языками.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *