Чому індексація в масиві починається з нуля в C, а не з 1?
Чому індексація в масиві починається з нуля в C, а не з 1?
Відповіді:
У C ім'я масиву є по суті вказівником [але див. Коментарі] , посиланням на місце пам'яті і тому вираз array[n]
посилається на n
елементи розташування пам’яті подалі від початкового елемента. Це означає, що індекс використовується як зміщення. Перший елемент масиву точно міститься в місці пам'яті, на який посилається масив (0 елементів), тому його слід позначати як array[0]
.
Для отримання додаткової інформації:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
sizeof arr
дає розмір об’єкта масиву, а не розмір вказівника.
sizeof
оператора або унарний &
оператор, або це рядковий літерал, який використовується для ініціалізації масиву, вираз, що має тип" масив типу "перетворюється на вираз з типом" вказівник на тип ", який вказує на початковий елемент об'єкта масиву і не є значенням. Якщо об'єкт масиву має клас зберігання в регістрі, поведінка не визначена. "
Це питання було розміщено більше року тому, але ось ...
Хоча стаття Dijkstra (раніше згадувана у видаленій відповіді ) має сенс з математичної точки зору, вона не так актуальна, що стосується програмування.
Рішення, яке приймають мовні специфікації та розробники-компілятори, ґрунтується на рішенні, прийнятому дизайнерами комп'ютерних систем, починати рахувати з 0.
Цитуючи Заклик до миру Данні Коена.
Для будь-якої основи b перші b ^ N невід’ємні цілі числа представлені точно N цифрами (включаючи провідні нулі), лише якщо нумерація починається з 0.
Це можна перевірити досить легко. У базі-2 візьміть 2^3 = 8
восьме число:
111
може бути представлений за допомогою 3
бітів, при цьому 1000
буде потрібно додатковий біт (4 біта).
Адреси пам'яті комп'ютера мають 2^N
комірки, адресовані N
бітами. Тепер, якщо ми почнемо рахувати з 1, 2^N
клітинкам знадобляться N+1
рядки адреси. Додатковий біт потрібен для доступу до точно 1 адреси. ( 1000
у наведеному випадку.). Ще один спосіб вирішити це - залишити останню адресу недоступною та використовувати N
адресні рядки.
Обидва є неоптимальними рішеннями , порівняно з початковим підрахунком на 0, який би підтримував доступ усіх адрес, використовуючи точно N
адреси адреси!
Рішення почати рахувати з 0
цього часу пронизало всі цифрові системи , включаючи програмне забезпечення, що працює на них, оскільки це спрощує переклад коду на те, що може інтерпретувати базова система. Якби це не було, була б одна непотрібна операція перекладу між машиною та програмістом для кожного доступу до масиву. Це полегшує компіляцію.
Цитування з паперу:
a[b]
реалізовувалося, як і *(a+b)
в ранніх компіляторах. Навіть сьогодні ви можете все-таки писати 2[a]
замість a[2]
. Тепер, якщо індекси не почалися з 0, то a[b]
перетворилися б на *(a+b-1)
. Для цього було б потрібно 2 доповнення на процесорі того часу замість 0, що означає половину швидкості. Явно не бажано.
Тому що 0 - наскільки далеко від вказівника до голови масиву до першого елемента масиву.
Поміркуйте:
int foo[5] = {1,2,3,4,5};
Для доступу до 0 ми робимо:
foo[0]
Але foo розкладається на покажчик, і вищевказаний доступ має аналогічний арифметичний спосіб вказівника доступу до нього
*(foo + 0)
У наші дні арифметика вказівника використовується не так часто. Зворотній шлях, хоча, це був зручний спосіб взяти адресу і перемістити X "ints" від цієї вихідної точки. Звичайно, якщо ви хотіли просто залишитися там, де ви є, ви просто додасте 0!
Тому що індекс на основі 0 дозволяє ...
array[index]
... реалізується як ...
*(array + index)
Якби індекс базувався на 1, компілятору потрібно було б генерувати:, *(array + index - 1)
і це "-1" зашкодило б продуктивності.
Тому що це спростило компілятор і лінкер (простіше писати).
Довідка :
"... Посилальна пам'ять за адресою та зміщенням представлена безпосередньо в апаратних засобах практично в усіх архітектурах комп'ютерів, тому ця детальна конструкція в C полегшує компіляцію"
і
"... це робить простішою реалізацію ..."
Індекс масиву завжди починається з нуля. Припустимо, базова адреса - 2000. Тепер arr[i] = *(arr+i)
. Тепер if i= 0
це означає *(2000+0
) дорівнює базовій адресі або адресі першого елемента в масиві. цей індекс трактується як зсув, тому індекс за замовчуванням починається від нуля.
З тієї ж причини, що коли середа, і хтось запитує вас, скільки днів до середи, ви кажете 0, а не 1, а коли середа, а хтось запитує, скільки днів до четверга, ви кажете 1, а не 2.
Найбільш елегантне пояснення, яке я читав для нумерації на нульовому рівні, - це спостереження, що значення зберігаються не в позначених місцях рядка чисел, а в проміжках між ними. Перший елемент зберігається між нулем і одним, наступний між одним і двома і т. Д. N-й елемент зберігається між N-1 і N. Набір елементів може бути описаний за допомогою цифр з обох сторін. Індивідуальні предмети описуються за умовами, використовуючи числа під ним. Якщо одному задано діапазон (X, Y), ідентифікація окремих чисел за допомогою наведеного нижче числа означає, що можна ідентифікувати перший елемент без використання арифметики (це елемент X), але треба відняти один з Y для ідентифікації останнього елемента (Y -1). Визначення елементів за допомогою наведеного вище числа полегшило б ідентифікацію останнього елемента в діапазоні (це було б пункт Y),
Хоча не було б жахливо ідентифікувати предмети на основі числа над ними, визначення першого елемента в діапазоні (X, Y) як першого вище X, як правило, виходить більш приємним, ніж визначення його як нижнього (X + 1).
Спробуйте отримати доступ до піксельного екрану за допомогою координат X, Y на матриці на основі 1. Формула надзвичайно складна. Чому складний? Тому що ви перетворюєте координати X, Y в одне число, зміщення. Чому вам потрібно перетворити X, Y на зміщення? Тому що так організована пам'ять всередині комп'ютерів, як безперервний потік комірок пам'яті (масивів). Як комп’ютери мають справу з клітинками масиву? Використання зрушень (переміщення з першої комірки, нульова модель індексації).
Тож у якийсь момент коду, який вам потрібен (або потрібен компілятор), перетворити формулу 1-бази в формулу, засновану на 0, тому що так комп’ютери мають справу з пам'яттю.
Припустимо, ми хочемо створити масив розміром 5
int масиву [5] = [2,3,5,9,8],
нехай перший елемент масиву буде вказаний на розташування 100,
і ми вважатимемо, що індексація починається з 1, а не з 0.
тепер ми повинні знайти розташування 1-го елемента за допомогою індексу
(пам’ятаємо, що розташування 1-го елемента є 100),
оскільки розмір цілого числа є 4-розрядним,
тому -> враховуючи індекс 1, позиція буде
розміром індексу (1) * розмір цілого числа (4) = 4,
тому фактичне положення, яке воно нам покаже, становить
100 + 4 = 104
що не відповідає дійсності, оскільки початкове розташування було 100.
воно повинно вказувати на 100, а не на 104,
це неправильно,
припустимо, ми взяли індексацію з 0,
тоді
позиція 1-го елемента повинна бути
розміром індексу (0) * розмір цілого числа (4) = 0,
тому ->
розташування 1-го елемента становить 100 + 0 = 100,
і це було фактичне розташування елемента
, тому індексація починається з 0;
Сподіваюся, це зрозуміє вашу точку зору.
Я з яванського походження. Я представив відповідь на це питання на діаграмі, нижче якої я написав на аркуші паперу, який сам пояснює
Основні кроки:
Примітка : Блоки, показані на зображенні, є зображенням пам'яті
Перш за все вам потрібно знати, що масиви внутрішньо розглядаються як покажчики, оскільки "саме ім'я масиву містить адресу першого елемента масиву"
ex. int arr[2] = {5,4};
врахуйте, що масив починається з адреси 100, тому перший елемент елемента буде на адресу 100, а другий - на 104, врахуйте, що якщо індекс масиву починається з 1, тож
arr[1]:-
це можна записати у виразі покажчиків, як
arr[1] = *(arr + 1 * (size of single element of array));
врахуйте, що розмір int становить 4 байти,
arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);
як ми знаємо, ім'я масиву містить адресу його першого елемента, так arr = 100 зараз,
arr[1] = *(100 + 4);
arr[1] = *(104);
що дає,
arr[1] = 4;
через це вираження ми не можемо отримати доступ до елемента за адресою 100, який є першим офіційним елементом,
тепер розглянемо індекс масиву починається з 0, так
arr[0]:-
це буде вирішено як
arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);
тепер ми знаємо, що ім'я масиву містить адресу його першого елемента,
arr[0] = *(100);
що дає правильний результат
arr[0] = 5;
тому індекс масиву завжди починається від 0 у c.
довідково: всі деталі написані в книзі "Мова програмування C Брайаном Кернінгханом та Деннісом Рітчі"
У масиві індекс вказує відстань від початкового елемента. Отже, перший елемент знаходиться на відстані 0 від стартового елемента. Отже, тому масив починається з 0.
Це тому, що address
має вказувати право element
на масив. Припустимо, наведений нижче масив:
let arr = [10, 20, 40, 60];
Розглянемо тепер початок буття адреси 12
та розмір element
букви 4 bytes
.
address of arr[0] = 12 + (0 * 4) => 12
address of arr[1] = 12 + (1 * 4) => 16
address of arr[2] = 12 + (2 * 4) => 20
address of arr[3] = 12 + (3 * 4) => 24
Якби цього не було zero-based
, технічно нашим першим елементом елемента в array
би було те, 16
що неправильно, як це його місцезнаходження 12
.
Ім'я масиву - це постійний вказівник, що вказує на базову адресу. Коли ви використовуєте arr [i], компілятор маніпулює нею як * (arr + i). Діапазон int - від -128 до 127, компілятор вважає, що від -128 до -1 від’ємні числа та 0 до 128 - додатні числа. Отже, індекс масиву завжди починається з нуля.
int
Типу потрібно для підтримки щонайменше , 16-бітний діапазон, і на більшості систем ці дні підтримує 32 біта. Я думаю, що ваша логіка є хибною, і ваша відповідь справді не покращується щодо інших відповідей, які вже надали інші люди. Я пропоную видалити це.