Dlaczego indeksowanie w tablicy zaczyna się od zera w C, a nie od 1?
Dlaczego indeksowanie w tablicy zaczyna się od zera w C, a nie od 1?
Odpowiedzi:
W języku C nazwa tablicy jest zasadniczo wskaźnikiem [ale zobacz komentarze] , odniesieniem do miejsca w pamięci, a więc wyrażenie array[n]odnosi się do nelementów lokalizacji pamięci z dala od elementu początkowego. Oznacza to, że indeks jest używany jako przesunięcie. Pierwszy element tablicy jest dokładnie zawarty w lokalizacji pamięci, do której odwołuje się tablica (0 elementów od siebie), więc powinien być oznaczony jako array[0].
Po więcej informacji:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
sizeof arrzwraca rozmiar obiektu tablicy, a nie rozmiar wskaźnika.
sizeofoperatora, &operator jednoargumentowy lub literał tekstowy używany do inicjalizacji tablicy, wyrażenie o typie„ tablica ” typu "jest konwertowane na wyrażenie z typem" wskaźnik do typu ", które wskazuje na początkowy element obiektu tablicy i nie jest wartością l. Jeśli obiekt tablicy ma klasę pamięci rejestru, zachowanie jest niezdefiniowane. "
To pytanie zostało wysłane ponad rok temu, ale oto ...
Chociaż artykuł Dijkstry (wcześniej przywoływany w obecnie usuniętej odpowiedzi ) ma sens z matematycznego punktu widzenia, nie jest tak istotny, jeśli chodzi o programowanie.
Decyzja podjęta przez specyfikację języka i projektantów kompilatorów jest oparta na decyzji podjętej przez projektantów systemów komputerowych, aby rozpocząć liczenie od 0.
Cytat z błagania o pokój Danny'ego Cohena.
Dla dowolnej podstawy b pierwsze b ^ N nieujemnych liczb całkowitych jest reprezentowane przez dokładnie N cyfr (w tym zera wiodące) tylko wtedy, gdy numeracja zaczyna się od 0.
Można to dość łatwo przetestować. W podstawie 2 weź 2^3 = 8
ósma liczba to:
111może być reprezentowany za pomocą 3bitów, podczas gdy 1000będzie wymagał dodatkowego bitu (4 bity).
Adresy pamięci komputerów mają 2^Nkomórki adresowane Nbitami. Jeśli teraz zaczniemy liczyć od 1, 2^Nkomórki będą potrzebowały N+1linii adresu. Dodatkowy bit jest potrzebny, aby uzyskać dostęp do dokładnie 1 adresu. ( 1000w powyższym przypadku.). Innym sposobem rozwiązania tego problemu byłoby pozostawienie ostatniego adresu niedostępnego i użycie Nlinii adresowych.
Oba są nieoptymalnymi rozwiązaniami , w porównaniu do liczenia początkowego od 0, które zapewniłoby dostępność wszystkich adresów, używając dokładnie Nlinii adresowych!
Decyzja o rozpoczęciu liczenia 0od tego czasu przeniknęła wszystkie systemy cyfrowe , w tym działające na nich oprogramowanie, ponieważ ułatwia to przetłumaczenie kodu na to, co może zinterpretować system bazowy. Gdyby tak nie było, byłaby jedna niepotrzebna operacja translacji między maszyną a programistą dla każdego dostępu do tablicy. Ułatwia kompilację.
Cytując z artykułu:

a[b]został zaimplementowany tak jak *(a+b)we wczesnych kompilatorach. Nawet dzisiaj możesz nadal pisać2[a] zamiast tegoa[2] . Teraz, jeśli indeksy nie zaczynają się od 0 a[b], zamieniają się w *(a+b-1). Wymagałoby to 2 dodań na procesorach czasu zamiast 0, co oznacza połowę szybkości. Zdecydowanie niepożądane.
Ponieważ 0 to odległość od wskaźnika do początku tablicy do pierwszego elementu tablicy.
Rozważać:
int foo[5] = {1,2,3,4,5};
Aby uzyskać dostęp do 0, robimy:
foo[0]
Ale foo rozkłada się na wskaźnik, a powyższy dostęp ma analogiczny arytmetyczny sposób dostępu do wskaźnika
*(foo + 0)
Obecnie arytmetyka wskaźnikowa nie jest używana tak często. Dawno temu był to wygodny sposób na pobranie adresu i przeniesienie X „int” poza punkt początkowy. Oczywiście, jeśli chcesz po prostu zostać tam, gdzie jesteś, po prostu dodaj 0!
Ponieważ indeks oparty na 0 pozwala ...
array[index]
... do wdrożenia jako ...
*(array + index)
Gdyby indeks był oparty na 1, kompilator musiałby wygenerować:, *(array + index - 1)a to „-1” zaszkodziłoby wydajności.
Ponieważ uprościł kompilator i konsolidator (łatwiejszy do napisania).
„... Odwoływanie się do pamięci za pomocą adresu i przesunięcia jest reprezentowane bezpośrednio w sprzęcie na praktycznie wszystkich architekturach komputerowych, więc ten szczegół projektu w C ułatwia kompilację”
i
„… to upraszcza implementację…”
Indeks tablicy zawsze zaczyna się od zera. Załóżmy, że adres bazowy to 2000. Teraz arr[i] = *(arr+i). To if i= 0znaczy *(2000+0) jest równe adresowi bazowemu lub adresowi pierwszego elementu w tablicy. indeks ten jest traktowany jako przesunięcie, więc indeks głuchy zaczyna się od zera.
Z tego samego powodu, kiedy jest środa i ktoś pyta Cię, ile dni do środy, mówisz 0 zamiast 1, a kiedy jest środa i ktoś cię pyta, ile dni pozostało do czwartku, mówisz 1 zamiast 2.
Najbardziej eleganckim wyjaśnieniem numeracji od zera, jakie przeczytałem, jest spostrzeżenie, że wartości nie są przechowywane w zaznaczonych miejscach na osi liczbowej, ale raczej w odstępach między nimi. Pierwsza pozycja jest przechowywana między zerem a jednym, następna między jednym a dwoma itd. N-ty element jest przechowywany między N-1 a N. Zakres elementów można opisać numerami po obu stronach. Poszczególne pozycje są umownie opisane za pomocą numerów poniżej. Jeśli podano zakres (X, Y), identyfikacja poszczególnych numerów za pomocą numeru poniżej oznacza, że można zidentyfikować pierwszą pozycję bez użycia arytmetyki (to jest pozycja X), ale należy odjąć jedną od Y, aby zidentyfikować ostatnią pozycję (Y -1). Identyfikacja pozycji za pomocą powyższego numeru ułatwiłaby identyfikację ostatniej pozycji w zakresie (byłaby to pozycja Y),
Chociaż identyfikacja pozycji na podstawie liczby znajdującej się nad nimi nie byłaby okropna, zdefiniowanie pierwszej pozycji z zakresu (X, Y) jako pozycji znajdującej się powyżej X generalnie działa lepiej niż zdefiniowanie jej jako tej poniżej (X + 1).
Przyczyna techniczna może wynikać z faktu, że wskaźnikiem do lokalizacji pamięci tablicy jest zawartość pierwszego elementu tablicy. Jeśli zadeklarujesz wskaźnik z indeksem równym jeden, programy normalnie dodają wartość jeden do wskaźnika, aby uzyskać dostęp do treści, która nie jest tym, czego chcesz.
Spróbuj uzyskać dostęp do ekranu z pikselami, używając współrzędnych X, Y na macierzy o wartości 1. Formuła jest całkowicie złożona. Dlaczego jest złożony? Ponieważ kończy się konwersją współrzędnych X, Y na jedną liczbę, przesunięcie. Dlaczego musisz zamienić X, Y na przesunięcie? Ponieważ w ten sposób pamięć jest zorganizowana w komputerach jako ciągły strumień komórek pamięci (tablic). Jak komputery radzą sobie z komórkami macierzy? Korzystanie z przesunięć (przemieszczenia od pierwszej komórki, model indeksowania od zera).
Więc w pewnym momencie kodu, którego potrzebujesz (lub kompilator potrzebuje), aby przekonwertować formułę o podstawie 1 na formułę opartą na 0, ponieważ w ten sposób komputery radzą sobie z pamięcią.
Załóżmy, że chcemy utworzyć tablicę o rozmiarze 5
int array [5] = [2,3,5,9,8]
niech pierwszy element tablicy będzie wskazywał na położenie 100
i rozważmy indeksowanie zaczynające się od 1, a nie od 0.
teraz musimy znaleźć położenie pierwszego elementu za pomocą indeksu
(pamiętaj, że położenie pierwszego elementu to 100),
ponieważ rozmiar liczby całkowitej jest 4-bitowy,
więc -> biorąc pod uwagę indeks 1, pozycja byłaby
wielkości of index (1) * size of integer (4) = 4,
więc rzeczywista pozycja, którą nam pokaże, to
100 + 4 = 104
co nie jest prawdą, ponieważ początkowa lokalizacja była na 100.
powinna wskazywać na 100, a nie na 104
to jest błędne
teraz przypuśćmy, że wzięliśmy indeksowanie od 0
to
pozycja pierwszego elementu powinna mieć
rozmiar indeksu (0) * rozmiar liczby całkowitej (4) = 0,
więc ->
położenie 1-go elementu to 100 + 0 = 100
i taka była rzeczywista lokalizacja elementu,
dlatego indeksowanie zaczyna się od 0;
Mam nadzieję, że to wyjaśni Twój punkt widzenia.
Pochodzę z języka Java. Odpowiedź na to pytanie przedstawiłem na poniższym schemacie, który napisałem na kartce papieru, która jest oczywista
Główne kroki:
Uwaga : Bloki pokazane na obrazku to reprezentacja pamięci
przede wszystkim musisz wiedzieć, że tablice są wewnętrznie traktowane jako wskaźniki, ponieważ „sama nazwa tablicy zawiera adres pierwszego elementu tablicy”
ex. int arr[2] = {5,4};
weź pod uwagę, że tablica zaczyna się od adresu 100, więc element pierwszy będzie miał adres 100, a drugi będzie teraz 104, weź pod uwagę, że jeśli indeks tablicy zaczyna się od 1, więc
arr[1]:-
można to zapisać w wyrażeniu wskaźników w ten sposób:
arr[1] = *(arr + 1 * (size of single element of array));
Rozważ teraz rozmiar int to 4 bajty,
arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);
jak wiemy nazwa tablicy zawiera adres jej pierwszego elementu, więc arr = 100 teraz,
arr[1] = *(100 + 4);
arr[1] = *(104);
co daje,
arr[1] = 4;
z powodu tego wyrażenia nie możemy uzyskać dostępu do elementu pod adresem 100, który jest oficjalnym pierwszym elementem,
teraz rozważmy, że indeks tablicy zaczyna się od 0, więc
arr[0]:-
zostanie to rozwiązane jako
arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);
teraz wiemy, że nazwa tablicy zawiera adres jej pierwszego elementu, więc
arr[0] = *(100);
co daje poprawny wynik
arr[0] = 5;
dlatego indeks tablicy zawsze zaczyna się od 0 w c.
źródło: wszystkie szczegóły są opisane w książce „Język programowania C autorstwa briana kerninghana i dennisa ritchie”
W tablicy indeks wskazuje odległość od elementu początkowego. Zatem pierwszy element znajduje się w odległości 0 od elementu początkowego. Dlatego właśnie tablica zaczyna się od 0.
Dzieje się tak, ponieważ addressmusi wskazywać na prawo elementw tablicy. Załóżmy poniższą tablicę:
let arr = [10, 20, 40, 60];
Rozważmy teraz początek istnienia adresu 12i rozmiar elementbycia 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
Gdyby tak nie było zero-based, z technicznego punktu widzenia nasz pierwszy adres elementu w elemencie arraybyłby 16nieprawidłowy, ponieważ jego lokalizacja jest błędna 12.
Nazwa tablicy jest stałym wskaźnikiem wskazującym na adres bazowy. Kiedy używasz arr [i], kompilator przetwarza go jako * (arr + i). Ponieważ zakres int wynosi od -128 do 127, kompilator uważa, że od -128 do -1 to liczby ujemne i od 0 do 128 są liczbami dodatnimi, więc indeks tablicy zawsze zaczyna się od zera.
intTypu wymagane jest w zakresie przynajmniej 16-bitowego, a w większości systemów dzisiejszych czasach obsługuje 32 bitów. Myślę, że twoja logika jest błędna, a twoja odpowiedź naprawdę nie poprawia innych odpowiedzi już udzielonych przez innych ludzi. Proponuję to usunąć.