Pourquoi l'indexation commence-t-elle par zéro en «C»?

154

Pourquoi l'indexation dans un tableau commence-t-elle par zéro en C et non par 1?

Nitish Pareek
la source
7
Tout est question de pointeurs!
médaille du
3
duplication possible des tableaux de base zéro Defend
dmckee --- ex-moderator chaton
3
Un pointeur (tableau) est une direction de mémoire et un index est un décalage de cette direction de mémoire, de sorte que le premier élément du pointeur (tableau) est celui dont le décalage est égal à 0.
D33pN16h7
3
@drhirsch parce que quand on compte un ensemble d'objets, on commence par pointer un objet et dire "un".
phoog le
1
Les Américains comptent les étages (étages) d'un immeuble à partir de celui du rez-de-chaussée; les Britanniques comptent de zéro (rez-de-chaussée), remontant au premier étage, puis au deuxième étage, etc.
Jonathan Leffler

Réponses:

116

En C, le nom d'un tableau est essentiellement un pointeur [mais voyez les commentaires] , une référence à un emplacement mémoire, et donc l'expression se array[n]réfère à un emplacement mémoire des néléments éloignés de l'élément de départ. Cela signifie que l'index est utilisé comme décalage. Le premier élément du tableau est exactement contenu dans l'emplacement mémoire auquel le tableau fait référence (à 0 élément), il doit donc être notéarray[0] .

Pour plus d'informations:

http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html

Massimiliano Peluso
la source
20
Le nom d'un tableau est le nom du tableau; contrairement à l'idée fausse commune, les tableaux ne sont en aucun cas des pointeurs. Une expression de tableau (comme le nom d'un objet de tableau) est généralement, mais pas toujours , convertie en un pointeur vers le premier élément. Exemple: sizeof arrdonne la taille de l'objet tableau, pas la taille d'un pointeur.
Keith Thompson
Bien que vous n'ayez évidemment pas réagi au commentaire de @ KeithThompson, j'aimerais que vous utilisiez un cours plus offensif: " En C, le nom d'un tableau est essentiellement un pointeur, une référence à un emplacement mémoire " - Non, ce n'est pas . Du moins pas d'un point de vue générique. Bien que votre réponse parfaite réponde à la question d'une manière en quoi 0 comme début d'index est important, la première phrase est tout simplement incorrecte. Un tableau ne se désintègre pas toujours en un pointeur vers son premier élément.
RobertS soutient Monica Cellio le
Citation du standard C, (C18), 6.3.2.1/4: " Sauf quand c'est l'opérande de l' sizeofopérateur, ou l' &opérateur unaire , ou est une chaîne littérale utilisée pour initialiser un tableau, une expression qui est de type" tableau de type "est converti en une expression de type" pointeur sur type "qui pointe vers l'élément initial de l'objet tableau et n'est pas une valeur l. Si l'objet tableau a une classe de stockage de registre, le comportement n'est pas défini. "
RobertS prend en charge Monica Cellio
Cette désintégration se produit également d'une manière plus «implicite» ou «formelle» que ce qui est suggéré ici; il n'y a pas de désintégration vers un objet pointeur en mémoire impliqué. C'est l'objet de cette question: le tableau vers la désintégration du pointeur est-il changé en objet pointeur? - Veuillez modifier votre réponse pour qu'elle soit entièrement correcte.
RobertS soutient Monica Cellio le
103

Cette question a été postée il y a plus d'un an, mais voilà ...


À propos des raisons ci-dessus

Bien que l'article de Dijkstra (précédemment référencé dans une réponse maintenant supprimée ) ait du sens d'un point de vue mathématique, il n'est pas aussi pertinent en matière de programmation.

La décision prise par la spécification du langage et les concepteurs de compilateurs est basée sur la décision prise par les concepteurs de systèmes informatiques de commencer à compter à 0.


La raison probable

Citation d' un plaidoyer pour la paix par Danny Cohen.

Pour toute base b, les premiers b ^ N entiers non négatifs sont représentés par exactement N chiffres (y compris les zéros non significatifs) uniquement si la numérotation commence à 0.

Cela peut être testé assez facilement. En base-2, prenez 2^3 = 8 Le 8ème nombre est:

  • 8 (binaire: 1000) si nous commençons à compter à 1
  • 7 (binaire: 111) si nous commençons à compter à 0

111peut être représenté en utilisant des 3bits, alors qu'il 1000faudra un bit supplémentaire (4 bits).


Pourquoi est-ce pertinent

Les adresses mémoire de l'ordinateur ont des 2^Ncellules adressées par Nbits. Maintenant, si nous commençons à compter à 1, les 2^Ncellules auraient besoin de N+1lignes d'adresse. Le bit supplémentaire est nécessaire pour accéder à exactement 1 adresse. ( 1000dans le cas ci-dessus.). Une autre façon de résoudre ce problème serait de laisser la dernière adresse inaccessible et d'utiliserN des lignes d'adresse.

Les deux sont des solutions sous-optimales , par rapport au décompte de départ à 0, qui garderait toutes les adresses accessibles, en utilisant exactement Nles lignes d'adresse!


Conclusion

La décision de commencer à compter à 0, a depuis imprégné tous les systèmes numériques , y compris les logiciels qui les exécutent, car elle simplifie la traduction du code en ce que le système sous-jacent peut interpréter. Si ce n'était pas le cas, il y aurait une opération de traduction inutile entre la machine et le programmeur, pour chaque accès au tableau. Cela facilite la compilation.


Citant le papier:

entrez la description de l'image ici

Anirudh Ramanathan
la source
2
Et s'ils venaient de supprimer le bit 0 ... alors le 8ème numéro serait toujours 111 ...
DanMatlin
2
Proposez-vous en fait une modification de l'arithmétique de base pour l'adapter? Ne pensez-vous pas que ce que nous avons aujourd'hui est une bien meilleure solution?
Anirudh Ramanathan
Des années plus tard, mes 2 cnt valent. D'après mon expérience (~ 35 ans de programmation), l'opération d'addition modulo ou modulaire sous une forme ou une autre revient étonnamment souvent. Avec une base zéro, le suivant dans la séquence est (i + 1)% n mais avec la base 1, il s'agit de (i-1)% n) +1, donc je pense que la base 0 est préférée. Cela revient assez souvent en mathématiques et en programmation. C'est peut-être juste moi ou le domaine dans lequel je travaille.
nyholku
Bien que toutes les bonnes raisons, je pense que c'est beaucoup plus simple: a a[b]été implémenté comme *(a+b)dans les premiers compilateurs. Même aujourd'hui, vous pouvez toujours écrire à la 2[a]place de a[2]. Maintenant, si les index ne commençaient pas à 0, ils a[b]deviendraient *(a+b-1). Cela aurait nécessité 2 ajouts sur les processeurs de l'époque au lieu de 0, soit la moitié de la vitesse. Clairement pas souhaitable.
Goswin von Brederlow
1
Ce n'est pas parce que vous voulez 8 états que vous devez avoir le numéro 8. Les interrupteurs d'éclairage de ma maison sont heureux de représenter les états «allumé», «éteint», sans jamais se demander pourquoi ils ne représentent pas le chiffre 2.
Spyryto
27

Parce que 0 est la distance entre le pointeur de la tête du tableau et le premier élément du tableau.

Considérer:

int foo[5] = {1,2,3,4,5};

Pour accéder à 0, nous faisons:

foo[0] 

Mais foo se décompose en un pointeur, et l'accès ci-dessus a une manière arithmétique de pointeur analogue d'y accéder

*(foo + 0)

De nos jours, l'arithmétique des pointeurs n'est pas utilisée aussi fréquemment. Il y a bien longtemps, c'était un moyen pratique de prendre une adresse et d'éloigner X "ints" de ce point de départ. Bien sûr, si vous vouliez rester là où vous êtes, il vous suffit d'ajouter 0!

Doug T.
la source
23

Parce que l'index basé sur 0 permet ...

array[index]

... à mettre en œuvre comme ...

*(array + index)

Si l'index était basé sur 1, le compilateur aurait besoin de générer:, *(array + index - 1)et ce "-1" nuirait aux performances.

Branko Dimitrijevic
la source
4
Tu amènes un point intéressant. Cela peut nuire aux performances. Mais la performance touchée sera-t-elle significative pour justifier l'utilisation de 0 comme indice de départ? J'en doute.
FirstName LastName
3
Les index basés sur @FirstNameLastName 1 n'offrent aucun avantage sur les index basés sur 0, mais ils fonctionnent (légèrement) moins bien. Cela justifie les index basés sur 0, quelle que soit la «petite» valeur du gain. Même si les index basés sur 1 offraient un certain avantage, c'est dans l'esprit du C ++ de choisir la performance plutôt que la commodité. C ++ est parfois utilisé dans des contextes où chaque dernière partie de la performance compte, et ces «petites» choses peuvent rapidement s'additionner.
Branko Dimitrijevic
Oui, je comprends que de petites choses peuvent s'additionner et parfois devenir une grande chose. Par exemple, 1 $ par an, ce n'est pas beaucoup d'argent. Mais si 2 milliards de personnes en font don, nous pouvons faire beaucoup de bien à l'humanité. Je recherche un exemple similaire dans le codage qui pourrait entraîner de mauvaises performances.
FirstName LastName
2
Plutôt que de soustraire 1, vous devez utiliser l'adresse du tableau-1 comme adresse de base. C'est ce que nous avons fait dans un compilateur sur lequel j'ai travaillé une fois. Cela élimine la soustraction à l'exécution. Lorsque vous écrivez un compilateur, ces instructions supplémentaires comptent beaucoup. Le compilateur sera utilisé pour générer des milliers de programmes, chacun pouvant être utilisé des milliers de fois, et cette instruction supplémentaire peut apparaître sur plusieurs lignes à l'intérieur d'une boucle au carré n. Cela peut représenter des milliards de cycles perdus.
progrmr
Non, cela ne nuira pas aux performances une fois compilé, cela ne fera qu'ajouter un petit temps de construction car à la fin, il sera traduit en code machine. Cela ne fera que blesser les concepteurs du compilateur.
Hassaan Akbar
12

Parce que cela a simplifié le compilateur et l'éditeur de liens (plus facile à écrire).

Référence :

"... Le référencement de la mémoire par une adresse et un décalage est représenté directement dans le matériel sur pratiquement toutes les architectures informatiques, donc ce détail de conception en C facilite la compilation"

et

"... cela simplifie la mise en œuvre ..."

progrmr
la source
1
+1 Je ne sais pas pourquoi les votes négatifs. Bien qu'elle ne réponde pas directement à la question, l'indexation basée sur 0 n'est pas naturelle pour les gens ou les mathématiciens - la seule raison pour laquelle cela est fait est que l'implémentation est logiquement cohérente (simple).
phkahler le
4
@phkahler: l'erreur est dans les auteurs et les langages appelant les indices de tableau comme indices; si vous le considérez comme un décalage, alors la base 0 devient également naturelle pour les profanes. Considérez l'horloge, la première minute s'écrit 00:00, pas 00:01 n'est-ce pas?
Lie Ryan
3
+1 - c'est probablement la réponse la plus correcte. C est antérieur à celui de Djikistras et était l'une des premières langues "à 0". C a commencé sa vie "en tant qu'assembleur de haut niveau" et il est probable que K & R veuille s'en tenir aussi étroitement à la façon dont il a été fait dans l'assembleur où vous auriez normalement une adresse de base plus un décalage commençant à zéro.
James Anderson
Je pensais que la question était de savoir pourquoi la base 0 était utilisée, non ce qui est mieux.
progrmr
2
Je ne rejetterai pas mais comme le programme l'a commenté ci-dessus, la base peut être prise en charge en ajustant l'adresse des tableaux, donc quel que soit le temps d'exécution de la base, c'est le même et c'est facile à implémenter dans le compilateur ou l'interpréteur, donc cela ne simplifie pas vraiment la mise en œuvre . Témoin Pascal où vous pouvez utiliser n'importe quelle plage pour l'indexation IIRC, cela fait 25 ans;)
nyholku
5

L'index de tableau commence toujours par zéro. Supposons que l'adresse de base est 2000. Maintenant arr[i] = *(arr+i). Maintenant if i= 0, cela signifie *(2000+0) est égal à l'adresse de base ou à l'adresse du premier élément du tableau. cet index est traité comme un offset, donc l'index bydeafault commence à zéro.

Amit Prakash
la source
5

Pour la même raison que, quand c'est mercredi et que quelqu'un vous demande combien de jours jusqu'à mercredi, vous dites 0 au lieu de 1, et que quand c'est mercredi et que quelqu'un vous demande combien de jours avant jeudi, vous dites 1 plutôt que 2.

R .. GitHub STOP AIDING ICE
la source
6
Votre réponse semble juste une question d'opinion.
heltonbiker
6
Eh bien, c'est ce qui fait que l'ajout d'indices / de compensations fonctionne. Par exemple, si «aujourd'hui» vaut 0 et «demain» vaut 1, «demain de demain» vaut 1 + 1 = 2. Mais si «aujourd'hui» est 1 et «demain» est 2, «demain de demain» n'est pas 2 + 2. Dans les tableaux, ce phénomène se produit chaque fois que vous voulez considérer une sous-plage d'un tableau comme un tableau à part entière.
R .. GitHub STOP HELPING ICE
7
Appeler une collection de 3 choses "3 choses" et les numéroter 1, 2, 3 n'est pas une lacune. Les numéroter avec un décalage par rapport au premier n'est pas naturel, même en mathématiques. Le seul moment où vous indexez à partir de zéro en mathématiques est lorsque vous souhaitez inclure quelque chose comme la puissance zéro (terme constant) dans un polynôme.
phkahler
9
Re: "La numérotation des tableaux commençant par 1 plutôt que par 0 est destinée aux personnes ayant un grave déficit de pensée mathématique." Mon édition de "Introduction aux algorithmes" de CLR utilise l'indexation de tableau basée sur 1; Je ne pense pas que les auteurs aient un manque de pensée mathématique.
RexE
Non, je dirais que le septième est à l'indice 6, ou à 6 positions du premier.
R .. GitHub STOP HELPING ICE
2

L'explication la plus élégante que j'ai lue pour la numérotation à base zéro est une observation selon laquelle les valeurs ne sont pas stockées aux endroits marqués sur la droite numérique, mais plutôt dans les espaces entre elles. Le premier élément est stocké entre zéro et un, le suivant entre un et deux, etc. Le Nième élément est stocké entre N-1 et N. Une plage d'éléments peut être décrite en utilisant les nombres de chaque côté. Les éléments individuels sont par convention décrits en utilisant les numéros ci-dessous. Si l'on donne une plage (X, Y), l'identification des numéros individuels à l'aide du nombre ci-dessous signifie que l'on peut identifier le premier élément sans utiliser d'arithmétique (c'est l'élément X) mais il faut en soustraire un à Y pour identifier le dernier élément (Y -1). Identifier les éléments à l'aide du numéro ci-dessus faciliterait l'identification du dernier élément d'une plage (ce serait l'élément Y),

Bien qu'il ne soit pas horrible d'identifier les éléments en fonction du nombre au-dessus d'eux, définir le premier élément de la plage (X, Y) comme étant celui au-dessus de X fonctionne généralement mieux que de le définir comme celui ci-dessous (X + 1).

supercat
la source
1

La raison technique peut provenir du fait que le pointeur vers un emplacement mémoire d'un tableau est le contenu du premier élément du tableau. Si vous déclarez le pointeur avec un index de un, les programmes ajouteraient normalement cette valeur de un au pointeur pour accéder au contenu qui n'est pas ce que vous voulez, bien sûr.

Rob
la source
1

Essayez d'accéder à un écran de pixels en utilisant les coordonnées X, Y sur une matrice basée sur 1. La formule est tout à fait complexe. Pourquoi est complexe? Parce que vous finissez par convertir les coordonnées X, Y en un seul nombre, le décalage. Pourquoi avez-vous besoin de convertir X, Y en offset? Parce que c'est ainsi que la mémoire est organisée à l'intérieur des ordinateurs, en tant que flux continu de cellules mémoire (tableaux). Comment les ordinateurs traitent les cellules de matrice? Utilisation de décalages (déplacements à partir de la première cellule, modèle d'indexation à base zéro).

Donc, à un moment donné du code, vous avez besoin (ou le compilateur a besoin) de convertir la formule de base 1 en une formule de base 0 parce que c'est ainsi que les ordinateurs traitent la mémoire.

Gianluca Ghettini
la source
1

Supposons que nous voulions créer un tableau de taille 5
int array [5] = [2,3,5,9,8]

que le 1er élément du tableau soit pointé à l'emplacement 100

et considérons que l'indexation commence à 1 et non à 0.

maintenant nous devons trouver l'emplacement du 1er élément à l'aide de l'index
(rappelez-vous que l'emplacement du 1er élément est 100)

puisque la taille d'un entier est de 4 bits
donc -> en considérant l'index 1, la position serait la
taille of index (1) * size of integer (4) = 4
donc la position réelle qu'il nous montrera est

100 + 4 = 104

ce qui n'est pas vrai parce que l'emplacement initial était à 100.
il devrait pointer vers 100 et non vers 104
c'est faux

maintenant supposons que nous ayons pris l'indexation à partir de 0
alors la
position du 1er élément devrait être la
taille de l'index (0) * taille de l'entier (4) = 0

donc -> l'
emplacement du 1er élément est 100 + 0 = 100

et c'était l'emplacement réel de l'élément,
c'est pourquoi l'indexation commence à 0;

J'espère que cela clarifiera votre point de vue.

sahil singh
la source
1

Je viens d'un fond Java. J'ai présenté la réponse à cette question dans le diagramme ci-dessous que j'ai écrit sur une feuille de papier qui s'explique d'elle-même

Principales étapes:

  1. Créer une référence
  2. Instanciation du tableau
  3. Allocation des données au tableau

  • Notez également lorsque le tableau est simplement instancié .... Zéro est alloué à tous les blocs par défaut jusqu'à ce que nous lui attribuions une valeur
  • Le tableau commence par zéro car la première adresse pointera vers la référence (i: e - X102 + 0 dans l'image)

entrez la description de l'image ici

Remarque : les blocs affichés dans l'image sont une représentation en mémoire

Devrath
la source
0

tout d'abord il faut savoir que les tableaux sont considérés en interne comme des pointeurs car le "nom du tableau lui-même contient l'adresse du premier élément du tableau"

ex. int arr[2] = {5,4};

considérez que le tableau commence à l'adresse 100 donc le premier élément sera à l'adresse 100 et le second sera à 104 maintenant, considérez que si l'index du tableau commence à 1, donc

arr[1]:-

cela peut être écrit dans l'expression des pointeurs comme ceci-

 arr[1] = *(arr + 1 * (size of single element of array));

considérez que la taille de int est de 4 octets, maintenant,

arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);

comme nous le savons, le nom du tableau contient l'adresse de son premier élément donc arr = 100 maintenant,

arr[1] = *(100 + 4);
arr[1] = *(104);

qui donne,

arr[1] = 4;

à cause de cette expression, nous ne pouvons pas accéder à l'élément à l'adresse 100 qui est le premier élément officiel,

considérez maintenant que l'index du tableau commence à 0, donc

arr[0]:-

cela sera résolu comme

arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);

maintenant, nous savons que le nom du tableau contient l'adresse de son premier élément donc,

arr[0] = *(100);

qui donne un résultat correct

arr[0] = 5;

par conséquent, l'index du tableau commence toujours à 0 en c.

référence: tous les détails sont écrits dans le livre "Le langage de programmation C par brian kerninghan et dennis ritchie"

akshay chaudhari
la source
0

Dans un tableau, l'index indique la distance par rapport à l'élément de départ. Ainsi, le premier élément est à 0 distance de l'élément de départ. C'est pourquoi le tableau commence à 0.

Rishi Raj Tandon
la source
0

C'est parce que le addressdoit pointer vers la droite elementdans le tableau. Supposons le tableau ci-dessous:

let arr = [10, 20, 40, 60]; 

Considérons maintenant le début de l'adresse étant 12et la taille de l' elementêtre 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

Si ce n'était pas le cas zero-based , techniquement, notre premier élément d'adresse dans le arrayserait ce 16qui est faux car son emplacement est 12.

Thalaivar
la source
-2

Le nom du tableau est un pointeur constant pointant vers l'adresse de base. Lorsque vous utilisez arr [i], le compilateur le manipule comme * (arr + i). Puisque la plage int est de -128 à 127, le compilateur pense que -128 à -1 sont les nombres négatifs et 0 à 128 sont des nombres positifs, donc l'index de tableau commence toujours par zéro.

Niks
la source
1
Qu'entendez-vous par «intervalle int compris entre -128 et 127» ? Un inttype est requis pour prendre en charge au moins une plage de 16 bits, et sur la plupart des systèmes de nos jours prend en charge 32 bits. Je pense que votre logique est erronée et que votre réponse ne s'améliore pas vraiment par rapport aux autres réponses déjà fournies par d'autres personnes. Je suggère de supprimer ceci.
Jonathan Leffler