Une question posée ici m'a rappelé une discussion que j'ai eue avec un collègue programmeur. Il a fait valoir que les tableaux à base zéro devraient être remplacés par des tableaux à base unique, car les tableaux à base zéro sont un détail d'implémentation qui provient de la façon dont les tableaux et les pointeurs et le matériel informatique fonctionnent, mais ce genre de choses ne devrait pas être reflété à un niveau supérieur les langues.
Maintenant, je ne suis pas vraiment doué pour débattre, donc je ne pouvais pas vraiment offrir de bonnes raisons de rester avec des tableaux à base zéro, à part qu'ils se sentent plus appropriés. Pourquoi est zéro le point de départ commun pour les tableaux?
Je ne pense pas qu'aucun de nous puisse fournir un argument plus fort que l'article d'Edsger W. Dijkstra "Pourquoi la numérotation devrait commencer à zéro" .
Eh bien ... Apparemment, la plupart des langues, y compris les plus récentes, sont à base zéro. Comme ces langues ont été écrites par des gens assez qualifiés, votre ami doit se tromper ...
pourquoi 1 serait un meilleur indice de départ que zéro? Pourquoi pas 2 ou 10? La réponse elle-même est intéressante car elle en montre beaucoup sur le processus de réflexion des personnes qui défendent l'idée.
Le première l'argument est qu'il est plus naturel, car le 1er est généralement le une avant tous les autres, du moins, pour la majorité des gens ...
Le nombre-une l'argument est que le dernier index est également la taille du tableau ...
Je suis toujours impressionné par la "qualité" des raisons que j'entends habituellement pour ce genre d'arguments ... Et encore plus quand on me le rappelle ...
... Les notations "à base unique" sont des restes de la culture occidentale qui ont ignoré l'existence du zéro pendant des siècles, sinon plus.
Croyez-le ou non, le calendrier grégorien d'origine va de -3, -2, -1, 1, 2, 3 ... Essayez d'imaginer le problème qu'il a contribué à la science occidentale (par exemple, combien d'années à partir du 1er janvier -2 au 1er janvier 2 pour voir que le calendrier grégorien d'origine entre en conflit avec quelque chose d'aussi simple que la soustraction ...).
Rester sur des tableaux à base unique, c'est comme (eh bien, je serai rétrogradé pour ça ... ^ _ ^ ...), garder des miles et des yards au 21ème siècle ...
Première (Oups ... Désolé ... je vais réessayer)
Zéro, Zéro n'est rien, on est quelque chose. Et certains textes religieux affirment que "Au début, il n'y avait rien". Certaines discussions informatiques peuvent être aussi brûlantes que les débats religieux, donc ce point n'est pas aussi hors sujet qu'il n'y paraît ... ^ _ ^
Première, Il est plus facile de travailler avec un tableau à base zéro et d'ignorer sa valeur zéro que de travailler avec un tableau à base un et de pirater pour trouver sa valeur zéro. Cette raison est presque aussi stupide que la précédente, mais l'argument initial en faveur des tableaux à base unique était également tout à fait faux.
Seconde, Souvenons-nous que lorsque vous traitez avec des nombres, les chances sont élevées que vous traitez avec les mathématiques à un moment ou à un autre, et lorsque vous traitez avec les mathématiques, les chances sont bonnes que vous n'êtes pas d'humeur à ce que des hacks stupides contournent les conventions obsolètes. La notation basée sur un a tourmenté les mathématiques et les dates pendant des siècles aussi, et en apprenant de nos erreurs, nous devrions nous efforcer de l'éviter dans les sciences orientées vers l'avenir (y compris les langages informatiques).
Troisième, Comme pour les tableaux de langage informatique liés au matériel, allouez un tableau C de 21 entiers et déplacez le pointeur de 10 index vers la droite, et vous aurez un tableau naturel [-10 à 10]. Ce n'est pas naturel pour le matériel. Mais c'est pour les mathématiques. Bien sûr, les mathématiques peuvent être obsolètes, mais la dernière fois que j'ai vérifié, la plupart des gens dans le monde pensaient que non.
Quatre, Comme déjà indiqué ailleurs, même pour une position discrète (ou des distances réduites à des valeurs discrètes), le premier indice serait zéro, comme le sol dans un bâtiment (à partir de zéro), le décompte décroissant (3, 2, 1, ZÉRO! ), l'altitude du sol, le premier pixel d'une image, la température (zéro Kelvin, pour le zéro absolu, ou zéro degré centigrade, comme une température de congélation de l'eau de 273 K). En fait, la seule chose qui commence vraiment par un est la manière traditionnelle de "première, seconde, troisième, etc." itération notation, ce qui me conduit naturellement à la suivant point...
Cinq le suivant (qui suit naturellement la précédent) est que les conteneurs de haut niveau doivent être accessibles, non pas par index, mais par itérateurs, sauf si les indices eux-mêmes ont une valeur intrinsèque. Je suis surpris que votre défenseur du "langage de niveau supérieur" n'en ait pas parlé. Dans le cas où l'indice lui-même est important, vous pouvez parier la moitié du temps que vous avez une question liée aux mathématiques en tête. Et donc, vous aimeriez que votre conteneur soit convivial pour les mathématiques, et non handicapé comme "votre ancien calendrier grégorien" à partir de 1, et nécessitant des hacks régurgités pour le faire fonctionner.
L'argument avancé par votre collègue programmeur est une erreur parce qu'il lie inutilement les habitudes linguistiques parlées/écrites, qui sont, par nature, floues, aux langages informatiques (où vous ne voulez pas que vos instructions soient floues), et parce qu'en attribuant à tort un matériel raison de ce problème, il espère vous convaincre, alors que les langues vont de plus en plus dans l'abstraction, que le tableau à base zéro appartient au passé.
Les tableaux à base zéro sont à base zéro pour des raisons liées aux mathématiques. Pas pour des raisons liées au matériel.
Maintenant, si cela pose un problème à votre collègue programmeur, demandez-lui de commencer à programmer avec de véritables constructions de haut niveau, comme des itérateurs et des boucles foreach.
Les intervalles semi-ouverts composent bien. Si vous avez affaire à 0 <= i < lim
et que vous souhaitez étendre par n
éléments, les nouveaux éléments ont des indices dans la plage lim <= i < lim + n
. Travailler avec des tableaux à base nulle rend l'arithmétique plus facile lorsque fractionnement ou concaténation tableaux ou lorsque comptage d'éléments. On espère que l'arithmétique plus simple conduit à moins d'erreurs de clôture.
Certains types de manipulation de tableaux deviennent compliqués avec les tableaux basés sur 1, mais restent plus simples avec les tableaux basés sur 0.
J'ai fait de la programmation d'analyse numérique à un moment donné. Je travaillais avec des algorithmes pour manipuler des matrices compressées et clairsemées, écrites à la fois en FORTRAN et en C++.
Les algorithmes FORTRAN avaient beaucoup de a[i + j + k - 2]
, alors que le C++ avait a[i + j + k]
, car le tableau FORTRAN était basé sur 1, tandis que le tableau C++ était basé sur 0.
L'index dans un tableau n'est pas vraiment un index. Il s'agit simplement d'un décalage qui correspond à la distance depuis le début du tableau. Le premier élément est au début du tableau donc il n'y a pas de distance. Par conséquent, le décalage est 0.
Les raisons ne sont pas seulement historiques: C et C++ sont toujours présents et largement utilisés et l'arithmétique des pointeurs est une raison très valable pour que les tableaux commencent à l'index 0.
Pour les autres langages dépourvus d'arithmétique des pointeurs, que le premier élément soit à l'index 0 ou 1 est plus une convention que n'importe quoi d'autre.
Le problème est que les langages qui utilisent l'index 1 comme premier élément n'existent pas dans le vide et doivent généralement interagir avec des bibliothèques qui sont souvent écrites en -vous l'avez deviné- C ou C++ ...
VB et ses saveurs dérivées ont souffert du fait que les tableaux commencent à 0 ou 1 et cela a été une source de problèmes pendant longtemps.
En fin de compte, peu importe ce que votre langue considère comme le premier index d'élément tant qu'il est cohérent. Le problème est que si l'on considère 1 comme premier indice, il est plus difficile de travailler avec dans la pratique.
Les tableaux à base zéro ont leurs racines en C et même en assembleur. Avec C, les mathématiques du pointeur fonctionnent comme suit:
Pour illustrer, supposons int a[4]
est à 0xFF00, les adresses sont:
Donc, avec des indices de base zéro, les mathématiques des adresses sont simples:
Adresse de l'élément = Adresse du tableau + index * sizeof (type)
En fait, les expressions en C sont toutes équivalentes:
Avec les tableaux à base unique, les calculs sont (toujours) légèrement plus compliqués.
Les raisons sont donc largement historiques.
Si vous utilisez des tableaux à base zéro, la longueur du tableau est l'ensemble des indices valides. C'est du moins ce que dit l'arithmétique de Peano:
0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}
C'est donc la notation la plus naturelle, dans un sens.
Parce qu'il existe une forte corrélation entre les tableaux et les pointeurs en C
char* p = "hello";
char q[] = "hello";
assert(p[1] == q[1]);
assert(*p == *q)
* p est identique à * (p + 0)
avoir un indice de départ de 1 vous donnera des maux de tête plus tard
Un tas est un exemple des avantages des tableaux basés sur 1. Étant donné un indice i, l'indice du parent et de l'enfant gauche de i est
PARENT
[i] = i ÷ 2
LCHILD
[i] = i × 2
Mais uniquement pour les tableaux basés sur 1. Pour les tableaux basés sur 0, vous avez
PARENT
[i] = (i + 1) ÷ 2 - 1
LCHILD
[i] = (i + 1) × 2 - 1
Et puis vous avez la propriété que i est également la taille du sous-tableau de cet index (c'est-à-dire les indices dans la plage [1, i]).
Mais au final, cela n'a pas d'importance, car vous pouvez transformer un tableau basé sur 0 en un tableau basé sur 1 en allouant un élément de plus que la normale et en ignorant le zéro. Ainsi, vous pouvez vous inscrire pour bénéficier des avantages des tableaux à base 1 le cas échéant, et conserver les tableaux à base 0 pour une arithmétique plus propre dans presque toutes les autres situations.
La raison pour laquelle il commence à 0 et non à 1 est que vous pouvez penser au décalage comme à quelle distance du début de la mémoire du tableau se trouve cet élément. Cela ne veut pas dire donnez-moi le 0e élément - c'est dire, donnez-moi l'élément qui est 0 éléments depuis le début.
Une autre façon de voir les choses est que celles-ci sont (pour la plupart) équivalentes:
array[n]
*(array + n)
La raison pour laquelle la norme ne sera jamais modifiée est que le C existe depuis environ 40 ans maintenant. Il n'y a aucune raison impérieuse de le changer et s'ils le faisaient, tout le code existant qui dépend du début du tableau étant à 0 serait cassé.
J'ai l'impression que c'est complètement arbitraire. Les tableaux à base zéro ou unique n'ont rien de spécial. Depuis que je me suis libéré de Visual Basic (la plupart du temps, parfois je fais de petites choses dans Excel), je n'ai pas travaillé avec des tableaux basés sur 1, et ... c'est la même chose. Le fait est que si vous avez besoin le troisième élément du tableau, c'est juste un détail d'implémentation qu'il s'appelle 3 ou 2. Cependant, 99% du travail que vous faites avec les tableaux ne s'intéresse qu'à deux points absolus: le premier élément et le nombre ou length. Encore une fois, c'est juste un détail d'implémentation que le premier élément est appelé zéro au lieu d'un, ou que le dernier élément est appelé count-1 ou, à la place, count.
Edit: Certains des répondants ont mentionné que les tableaux basés sur 1 sont plus sujets aux erreurs de clôture. D'après mon expérience, en y réfléchissant maintenant, c'est vrai. Je me souviens avoir pensé, en VB, "cela fonctionnera ou va exploser parce que je suis parti par un." En Java cela ne se produit jamais. Bien que je pensais que j'allais mieux, certains des répondeurs signalent des cas dans lesquels les tableaux basés sur 0 donnent une arithmétique plus agréable, MÊME lorsque vous n'avez pas à traiter avec une langue de niveau inférieur.
Le code comprenant des informations sur la position d'origine/position relative est beaucoup plus propre avec des tableaux commençant à 0.
Par exemple: Le code pour copier un vecteur à une position définie dans un vecteur plus grand est une douleur avec des tableaux commençant à 1:
function copyAtPos (dest, vect, i):
for i from 1 -> vect.length do
dest[pos+i-1] = vect[i]
Par opposition avec des tableaux commençant à 0:
function copyAtPos (dest, vect, i):
for i from 0 -> vect.length-1 do
dest[pos+i] = vect[i]
Si vous commencez à écrire de grandes formules de circonvolutions, cela devient un must.
En tant que programmeur C/C++ de 10 ans et plus, avec une solide expérience en Pascal et Delphi, je toujours manque la vérification du type de tableau et d'index du Pascal, ainsi que la flexibilité et la sécurité qui l'accompagnent. Un exemple évident de ceci est un tableau contenant des valeurs pour chaque mois.
Pascal:
Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);
Var Days[Month] of integer;
...
if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in Pascal and no i won't go look it up
Days[Feb] := 29
else
Days[Feb] := 28;
Écrire du code similaire en langage C sans utiliser les +/- 1 ou les "nombres magiques" est assez difficile. Notez que les expressions comme Days [2] et Days [Jan + Dec] ne se compileront tout simplement pas, ce qui peut sembler brutal aux personnes qui pensent encore en C ou en assembleur.
Je dois dire qu'il y a de nombreux aspects des langages Pascal/Delphi que je ne manque pas un peu, mais les tableaux basés sur C zéro semblent juste "stupides" en comparaison.
Pourquoi voulez-vous que les tableaux commencent à un?
Lorsque vous dites a[x][y]
, Le compilateur traduit cela en: a+(x*num_cols+y)
. Si les tableaux commençaient à un, cela deviendrait a+(x*num_cols+y-1)
. Ce serait une opération arithmétique supplémentaire à chaque fois vous voulez accéder à un élément de tableau. Pourquoi voudriez-vous ralentir les programmes?
Pourquoi pas 2 ou 3 ou 20? Ce n'est pas comme avoir des tableaux basés sur 1 est en quelque sorte plus facile ou plus simple à comprendre que les tableaux basés sur zéro. Afin de passer aux tableaux basés sur 1, chaque programmeur devrait réapprendre à travailler avec les tableaux.
De plus, lorsque vous traitez des décalages dans des tableaux existants, cela a également plus de sens. Si vous avez lu 115 octets dans un tableau, vous savez que le prochain bloc commence à 115. Et ainsi de suite, l'octet suivant est toujours la taille des octets que vous avez lus. Avec 1, vous devez en ajouter un tout le temps.
Et vous avez parfois besoin de traiter des morceaux de données dans des tableaux, même dans un langage sans "vrai" pointeur arithmétique. Dans Java vous pourriez avoir des données dans des fichiers mappés en mémoire ou des tampons. Dans ce cas, vous savez que le bloc i est de taille * i. Avec un index basé sur 1, ce serait au bloc * i +1.
Avec l'indexation basée sur 1, beaucoup de techniques nécessiteraient des +1 partout.
À l'aide de tableaux basés sur 1, transformez un tableau à une dimension en un tableau multidimensionnel:
int w = 5, h = 5, d = 5;
int[] a1 = new int[w * h * d], new a2 = int[w,h,d];
for (int z = 1; z <= d; z++)
for (int y = 1; y <= h; y++)
for (int x = 1; x <= w; x++)
a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];
Notez que vos index y et z sont basés sur 0 (y - 1, z - 1) même lorsque votre tableau est basé sur 1. Dans certaines circonstances, vous ne pouvez pas éviter les index basés sur 0. Par souci de cohérence, pourquoi ne pas toujours utiliser des index basés sur 0?
C'est à cause de la façon dont les tableaux sont construits. Cela n'a pas beaucoup de sens pour eux de commencer par un. Un tableau est une adresse de base en mémoire, une taille et un index. Pour accéder au nième élément, c'est:
base + n * element_size
Donc, 0 est évidemment le premier décalage.
Je vais sortir sur une branche ici et suggérer quelque chose de différent d'un tableau à clés entières.
Je pense que votre collègue est en train de créer une cartographie un à un d'un "ensemble" dans le monde physique où nous commençons toujours à compter à 1. Je peux comprendre cela, lorsque vous ne faites rien d'extraordinaire, il est facile de comprendre du code lorsque vous êtes mappé 1 à 1 entre le logiciel et le monde physique.
Ma suggestion
N'utilisez pas de tableaux basés sur des nombres entiers pour tout ce que vous stockez, mais utilisez un autre type de dictionnaire ou une paire de valeurs clés. Ils correspondent mieux à la vie réelle car vous n'êtes pas lié par un entier arbitraire. Cela a sa place et je recommanderais de l'utiliser autant que possible en raison des avantages de mapper les concepts 1 à 1 entre le logiciel et le monde physique.
c'est à dire. kvp['Name Server'] = "ns1.example.com";
(Ceci n'est qu'un exemple parmi un million d'exemples possibles).
Avertissement
Cela ne fonctionne certainement pas lorsque vous travaillez avec des concepts basés sur les mathématiques, essentiellement parce que les mathématiques sont plus proches de la mise en œuvre réelle d'un ordinateur. L'utilisation des ensembles kvp ne va rien aider ici, mais va réellement gâcher les choses et les rendre plus problématiques. Je n'ai pas réfléchi à tous les cas d'angle où quelque chose peut mieux fonctionner en kvp ou en tableau.
L'idée finale est d'utiliser les tableaux à base zéro ou les paires de valeurs clés là où cela a du sens, rappelez-vous que lorsque vous n'avez qu'un marteau, chaque problème commence à ressembler à un clou ...
Personnellement, le seul argument consiste à voir les index de tableau comme des décalages. C'est tout à fait logique.
On pourrait dire que c'est le premier élément, mais le décalage du premier élément par rapport à l'origine du tableau est nul. En tant que tel, prendre le tableau Origin et ajouter zéro produira le premier élément.
Ainsi, dans le calcul, il est plus facile d'ajouter zéro pour trouver le premier élément que d'en ajouter un, puis d'en supprimer un.
Je pense que quiconque a fait des trucs de niveau inférieur pense toujours à la base zéro. Et les personnes qui commencent ou sont habituées à un niveau supérieur de programmation souvent non algorithmique pourraient souhaiter un système de base. Ou peut-être que nous sommes simplement biaisés par les expériences passées.
Les deux seules raisons (très) sérieuses d'utiliser des indices basés sur 0 au lieu d'indices basés sur 1 semblent pour éviter de rééduquer beaucoup de programmeurs ET pour une compatibilité ascendante.
Je n'ai vu aucun autre argument sérieux contre les indices basés sur 1 dans toutes les réponses que vous avez reçues.
En fait, les indices sont naturellement basés sur 1, et voici pourquoi.
Tout d'abord, nous devons nous demander: d'où viennent les tableaux? Ont-ils des équivalents du monde réel? La réponse est oui: c'est ainsi que nous modélisons vecteurs et matrice en informatique. Cependant, les vecteurs et la matrice sont des concepts mathématiques qui utilisaient des indices basés sur 1 avant l'ère informatique (et qui utilisent encore aujourd'hui principalement des indices basés sur 1).
Dans le monde réel, les indices sont à 1 base.
Comme Thomas l'a dit plus haut, les langages qui utilisaient des indices à base 0 utilisent en fait décalages , pas des indices. Et les développeurs qui utilisent ces langages pensent à décalages , pas aux indices. Ce ne serait pas un problème si les choses étaient clairement énoncées, mais elles ne le sont pas. De nombreux développeurs utilisant des décalages parlent encore d'indices. Et beaucoup de développeurs utilisant des indices ne savent toujours pas que C, C++, C #, ... utilisent des décalages.
Il s'agit d'un problème de formulation.
(Remarque sur l'article de Diskstra - Il dit exactement ce que j'ai dit ci-dessus: mathématicien tilisez des indices basés sur 1. Mais Diskstra pense que les matématiciens ne devraient pas = les utiliser car une expression serait alors laide (par exemple: 1 <= n <= 0). Eh bien, je ne suis pas sûr qu'il ait raison là-dessus - faire un tel changement de paradigme afin d'éviter ces séquences vides exceptionnelles semble beaucoup des ennuis pour un petit résultat ...)
Avez-vous déjà été ennuyé par le fait que le "20e siècle" se réfère réellement aux années 1900? Eh bien, c'est une bonne analogie pour les choses fastidieuses que vous traitez tout le temps lorsque vous utilisez des tableaux basés sur 1.
Considérez une tâche de tableau commune comme la méthode de lecture .net IO.stream:
int Read(byte[] buffer, int offset, int length)
Voici ce que je vous suggère de faire pour vous convaincre que les tableaux basés sur 0 sont meilleurs:
Dans chaque style d'indexation, écrivez une classe BufferedStream qui prend en charge la lecture. Vous pouvez modifier la définition de la fonction de lecture (par exemple, utiliser une limite inférieure au lieu d'un décalage) pour les tableaux basés sur 1. Pas besoin de quoi que ce soit de fantaisiste, faites simplement simple.
Maintenant, laquelle de ces implémentations est la plus simple? Lequel a des décalages +1 et -1 saupoudrés ici et là? C'est ce que je pensais. En fait, je dirais que les seuls cas où le style d'indexation n'a pas d'importance, c'est quand vous auriez dû utiliser quelque chose qui n'était pas un tableau, comme un ensemble.
C'est possible si vous faites attention en écrivant votre "propre" code. Vous pouvez supposer que votre index commence à partir de n pour tout n> = 0 et programmer en conséquence.
En ce qui concerne la norme, Borealid a un excellent argument.
Un tableau en C est un raccourci pour l'arithmétique des pointeurs. Considérez ce cas:
struct Foo *foo;
struct Foo foos[10];
foo = &(foos[1]);
foo = foos + (1 * sizeof(struct Foo));
Les deux dernières lignes signifient la même chose. Changer le décalage initial briserait cette corrélation, rendant beaucoup de choses en C beaucoup plus difficiles.
Certaines langues avec un type de tableau très strict, comme Pascal, vous permettent de commencer à compter d'autres façons. Mais en C, et dans de nombreux langages dérivés de C, les tableaux sont simplement un raccourci pour l'arithmétique des pointeurs, vous ne pouvez donc pas jouer avec leur index de départ.
car les noms de tableau sont des pointeurs constants vers les positions de départ du tableau. Par exemple, en C, le tableau [2] est transformé en tableau + (sizeof (array) * 2), ce qui vous donnera deux éléments au-delà de l'élément de départ (troisième élément :)). donc si vous voulez atteindre l'élément de départ, avec les mêmes calculs, vous devez faire
tableau + (sizeof (tableau) * i) = tableau
(taille de (tableau) * i) = 0
i = 0
mathématiques d'équation simple.
Il existe en fait plusieurs façons de mettre en œuvre ceci:
En fin de compte, je ne pense pas qu'il soit important qu'un langage utilise des tableaux basés sur 0 ou 1. Mais, je pense que le meilleur choix est d'utiliser des tableaux basés sur 0, pour la simple raison que la plupart des programmeurs sont habitués à cette convention, et cela est compatible avec la grande majorité du code déjà écrit.
Cependant, la seule façon de vraiment vous tromper est d'être incohérent comme Visual Basic. La base de code que je gère actuellement est divisée entre les tableaux basés sur 0 et 1; et il est extrêmement difficile de déterminer lequel est lequel. Cela conduit à une boucle for verbeuse ennuyeuse:
dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
'...
next
Il en est ainsi, et ce depuis de nombreuses années. Le changer, ou même en débattre, est tout aussi inutile que de changer ou de débattre des feux de circulation. Faisons bleu = arrêter, rouge = aller.
Examinez les modifications apportées au fil du temps dans Recettes numériques pour C++. Ils avaient utilisé des macros pour simuler l'indexation basée sur 1, mais dans l'édition 2001, ils ont abandonné et rejoint le troupeau. Il peut y avoir du matériel éclairant sur les raisons derrière cela sur leur site www.nr.com
BTW, aussi ennuyeux sont les variantes de spécification d'une plage hors d'un tableau. Exemple: python vs IDL; a [100: 200] vs a [100: 199] pour obtenir 100 éléments. Il suffit d'apprendre les bizarreries de chaque langue. Pour changer une langue qui le fait une façon de faire correspondre l'autre causerait de tels coups et grincements de dents, et ne résoudrait aucun problème réel.
Le zéro est naturel quand on parle de emplacement d'un élément dans une collection linéaire.
Pensez à une étagère pleine de livres - le premier livre est situé au ras de la paroi latérale de l'étagère - c'est l'emplacement zéro.
Je suppose donc que cela dépend si vous considérez les indices matriciels comme un moyen de trouver des choses ou de s'y référer.
Je préfère un index basé sur 0 car puisque modulo (et l'opérateur AND lorsqu'il est utilisé pour modulo) renvoie toujours 0 pour certaines valeurs.
Je me retrouve souvent à utiliser des tableaux comme celui-ci:
int blah = array[i & 0xff];
Je me trompe souvent de ce type de code lorsque j'utilise des index basés sur 1.
Il est difficile de défendre la base 0 sans programmer beaucoup de code basé sur un tableau, comme la recherche de chaînes et divers algorithmes de tri/fusion, ou la simulation de tableaux multidimensionnels dans un tableau à une dimension. Fortran est basé sur 1, et vous avez besoin de beaucoup de café pour bien faire ce type de code.
Mais cela va bien au-delà. C'est une habitude mentale très utile de pouvoir penser à la longueur de quelque chose plutôt qu'aux indices de ses éléments. Par exemple, en faisant des graphiques basés sur des pixels, il est beaucoup plus clair de penser que les coordonnées se situent entre les pixels plutôt que sur eux. De cette façon, un rectangle 3x3 contient 9 pixels, pas 16.
Un exemple un peu plus farfelu est l'idée d'anticipation dans l'analyse syntaxique ou dans l'impression de sous-totaux dans un tableau. L'approche "de bon sens" dit 1) obtenir le caractère, le jeton ou la ligne de table suivant, et 2) décider quoi en faire. L'approche prospective dit 1) supposons que vous pouvez le voir, et décidez si vous le voulez, et 2) si vous le voulez, "acceptez" (ce qui vous permet de voir le suivant). Ensuite, si vous écrivez le pseudo-code, c'est beaucoup plus simple.
Encore un autre exemple est de savoir comment utiliser "goto" dans des langues où vous n'avez pas le choix, comme les fichiers batch MS-DOS. L'approche "de bon sens" consiste à attacher des étiquettes aux blocs de code à faire et à les étiqueter comme tels. Souvent, une meilleure approche consiste à placer des étiquettes aux extrémités des blocs de code, afin de les ignorer. Cela le rend "structuré" et beaucoup plus facile à modifier.
Je préfère les tableaux basés sur 0 car, comme mentionné par d'autres, cela facilite les mathématiques. Par exemple, si nous avons un tableau unidimensionnel de 100 éléments émulant une grille 10x10, alors quel est l'indice de tableau i de l'élément dans la ligne r, col c:
Base 0: i = 10 * r + c Base 1: i = 10 * (r - 1) + c
Et, étant donné l'indice i, revenir à la ligne et à la colonne est:
Base 0: c = i% 10 R = étage (i/10) Base 1: c = (i - 1)% 10 + 1 r = plafond (i/10)
Étant donné que les calculs ci-dessus sont clairement plus complexes lors de l'utilisation de tableaux basés sur 1, il semble logique de choisir des tableaux basés sur 0 comme standard.
Cependant, je pense que quelqu'un pourrait prétendre que ma logique est défectueuse parce que je suppose qu'il y aurait une raison de représenter des données 2D dans un tableau 1D. J'ai rencontré un certain nombre de ces situations en C/C++, mais je dois admettre que la nécessité d'effectuer de tels calculs dépend quelque peu du langage. Si les tableaux exécutaient vraiment toutes les mathématiques d'index pour le client, tout le temps, alors le compilateur pourrait simplement convertir vos accès aux tableaux basés sur M en base 0 au moment de la compilation et masquer tous ces détails d'implémentation à l'utilisateur. En fait, n'importe quelle constante de compilation pourrait être utilisée pour effectuer le même ensemble d'opérations, bien que de telles constructions conduiraient probablement à un code incompréhensible.
Un meilleur argument serait peut-être que la minimisation du nombre d'opérations d'index de tableau dans un langage avec des tableaux basés sur 1 nécessiterait qu'une division entière soit effectuée à l'aide de la fonction de plafond. Cependant, d'un point de vue mathématique, la division entière devrait renvoyer d reste r, où d et r sont tous deux positifs. Par conséquent, les tableaux basés sur 0 doivent être utilisés pour simplifier les mathématiques.
Par exemple, si vous générez une table de recherche avec N éléments, l'index le plus proche avant la valeur actuelle dans le tableau pour la valeur x serait (approximativement, en ignorant les valeurs où le résultat est un entier avant l'arrondi):
Base 0 avec étage: étage ((N - 1) * x/xRange) Base 1 avec étage: étage ((N - 1) * x/xRange) + 1 1 base avec plafond: plafond ((N - 1) * x/xRange)
Notez que si la convention standard d'arrondi est utilisée, les tableaux basés sur 1 nécessitent une opération supplémentaire, ce qui n'est pas souhaitable. Ce type de calcul ne peut pas être masqué par le compilateur, car il nécessite des connaissances de niveau inférieur sur ce qui se passe dans les coulisses.
Je parie que le programmeur était juste ennuyé par la contre-intuitivité d'un tableau basé sur 0 dans la pensée quotidienne et plaidait pour un moyen plus intuitif de décrire les tableaux. Je trouve ironique qu'en tant qu'êtres humains, nous ayons passé tellement de temps à trouver des "classes" afin de pouvoir décrire les choses de manière plus humaine dans notre code, mais quand nous regardons les tableaux 0 vs 1, nous semblons être accrochés la logique de cela seul.
En ce qui concerne l'ordinateur et mathématiquement, 0 va probablement être meilleur, mais je pense qu'un point manque ici. Si nous voulions décrire les choses d'une manière plus humaine (par exemple les cours), pourquoi ne voudrions-nous pas la même chose pour d'autres parties du langage? N'est-ce pas tout aussi logique ou valide (ou avoir une priorité plus élevée d'ailleurs ...) de rendre un langage plus facilement compréhensible et utilisable par les humains et donc, par extension, moins sujet aux scénarios qui ont tendance à créer des bugs logiques et plus enclin à une production plus rapide d'une création utilisable. PHP Exemple:
array(1 => 'January', 'February', 'March');
donne un tableau basé sur 1 selon notre demande.
Pourquoi pas ont la norme:
array('January', 'February', 'March');
Et l'exception soit:
array(0 => 'Value for scenario where 0 *has* to be used as the key',
'value2', 'value3');
Dans le cas de PHP par exemple, mon pari est de 80% du temps qu'un tableau basé sur 1 étant la syntaxe par défaut diminuerait les bugs logiques dans les cas d'utilisation du monde réel, ou du moins n'en causerait pas plus en moyenne, tout en le faisant beaucoup plus facile sur le codeur pour produire plus rapidement du code utilisable. Rappelez-vous, je suppose qu'il y aurait toujours l'option de tableau (0 => `` valeur '') lorsque cela est nécessaire, mais en supposant également que la majorité du temps, il est pratique d'avoir quelque chose de plus proche d'une description du monde réel.
Cela ne semble vraiment pas trop tiré par les cheveux d'une demande lorsque vous l'examinez dans cette perspective. Lorsque vous approchez d'une interface, que ce soit un système d'exploitation ou un langage pour un programmeur, plus la pensée humaine et les habitudes que nous concevons sont proches, plus nous serons heureux dans la plupart des cas et moins de malentendus entre l'homme et l'ordinateur (logique humaine - bugs), et la production plus rapide, etc. que nous aurons. Si 80% du temps dans le monde réel je décris des choses avec 1 lors de la création de listes ou du comptage, alors l'ordinateur devrait idéalement interpréter ma signification d'une manière qu'il comprend avec aussi peu d'informations ou changer de ma façon normale de décrire quelque chose que possible. En bref, plus nous pouvons modéliser le monde réel, meilleure est l'abstraction. Donc, ce qu'il veut n'est nullement stupide puisque c'est le but ultime et serait la preuve d'un besoin de plus d'abstraction. L'ordinateur peut toujours le voir comme une utilisation spéciale d'un tableau basé sur 0. Je me fiche de la façon dont l'ordinateur l'interprète tant qu'il s'agit d'un moyen plus simple et plus intuitif pour moi de décrire ce que je veux avec moins de bogues au fil du temps.
Voilà donc mes deux cents. Je doute sérieusement de ce qu'il disait, ou de ce qui a été interprété, de ce qu'il voulait dire. Il voulait probablement dire: "Je déteste avoir une façon moins intuitive de dire à l'ordinateur ce que je veux." :) Pas nous tous? lol.
Si vous insistez pour que vos tableaux commencent à 1 ...
type real_foo[COUNT], *const foo=real_foo-1;
Si vous êtes vraiment sadique, vous pouvez même créer une macro de préprocesseur pour le faire pour vous ...
#define CONCAT(x,y) x ## y
#define ARRAY1(name,size) CONCAT(real_, name), *const name=CONCAT(real_, name)-1
type ARRAY1(foo, COUNT);
J'espère que je n'ai pas foiré ces macros ...
C est un langage très simple (c'est ce qu'ils disent). Sa conception était fortement basée sur les fonctionnalités matérielles disponibles, c'est-à-dire sur la facilité d'implémentation du compilateur. L'indexation d'un tableau se traduit directement en arithmétique de pointeur, donc:
int array[256];
int i = 10;
...
array[i] = 12;
se traduit par quelque chose comme:
*(array + i*sizeof(int)) = 12;
ou en langage machine intermédiaire imaginaire:
load value of array into register Ra
load value of i into register Rb
right shift Rb by X number of bits
add value in Rb to value in Ra
store value 12 at address in Ra
(Remarque: ici array
est l'adresse fixée au moment du chargement, donc je dis "valeur de chargement de array
" - c'est un peu différent pour l'indexation d'un pointeur, une indirection supplémentaire est nécessaire pour obtenir le adresse de base.)
Dans cette optique, l'indexation à base zéro est tout à fait naturelle.
C'est certainement possible mais le langage C ne le supporte pas. Beaucoup d'autres langages tels que Fortran, PL/1, Pascal, Modula2, Ada le supportent.
Cela faisait partie de la conception du langage C de garder le compilateur simple et petit et cela casserait trop de choses pour le changer maintenant.
Vous pouvez, mais les optimisations du compilateur sont libres de créer des résultats invalides (gcc et msvc ne le seront pas, mais clang le fera).
char* myArray = malloc(100) - 1;
/* now myArray[1] is the first element, and myArray[100] is the last. */
free(myArray + 1);
Mais, comme d'autres l'ont mentionné, ne le faites pas. Désapprends tes mauvaises habitudes de commencer à 1.
Une autre solution: ignorez simplement le premier élément du tableau.
Je pense que lorsque vous essayez de réfléchir à la façon dont votre code fonctionne à un niveau inférieur, si cela devient nécessaire, les tableaux à base de zéro correspondent mieux à ce niveau inférieur. En outre, je suppose que vous auriez besoin d'une instruction d'ajout supplémentaire pour chaque déréférencement d'un tableau à base unique pour que les pointeurs fonctionnent correctement. Je vois un moyen de contourner cela (faites pointer le pointeur vers l'élément -1st/0th du tableau, pas le 0th/1st), mais cela pourrait faire des ravages avec les récupérateurs, car vous avez maintenant des pointeurs pointant en dehors de leurs blocs alloués.
Au niveau le plus atomique, c'est simplement dû au fait d'être binaire.
0 => 0
1 => 1
10 => 2
11 => 3
"0" est le premier nombre binaire, et les tableaux sont également indexés par leur "premier" nombre binaire.
En outre, étant donné une adresse de base arbitraire pour un ensemble de données, le premier enregistrement existe à cette adresse, pas à cette adresse + 1,
$base = 010110;
$firstvalue = $base + 0 * $unitsize
$secondvalue = $base + 1 * $unitsize
$thirdvalue = $base + 2 * $unitsize
Si vous aviez un tableau basé sur "1", le système interne devrait constamment décrémenter la valeur cible de 1 pour trouver l'adresse mémoire sous-jacente dans laquelle les données étaient stockées.
0 est tout autant le premier nombre binaire que le premier nombre décimal. Pas convaincant.
Écrivez le chiffre "0" sur une page. Combien de numéros avez-vous?
Si j'ai les numéros 500 à 550 sur la page, combien de numéros ai-je? J'en ai 51! Mais la différence n'est que de 50.
Mettez 50 moutons dans un enclos. Étonnamment, aucun d'entre eux ne ressemble à des chiffres, mais ils restent un volume dénombrable.
Nous avons cependant cette idée folle que lorsqu'un mouton ressemble un peu trop à un 0, il ne doit pas être utilisé et il doit être jeté et nous devons trouver un mouton ressemblant à 50 pour prendre sa place.
simplifié
Utilisez 1 à N pour compter. Mais 0 est toujours un symbole de lieu valide, et en tant que tel, il doit être utilisé.
Si 0 n'est pas un symbole de lieu valide, alors nous devons supprimer 10, 20, 30, 40 ... etc de notre système de numérotation, et passer directement de 9 à 11.
1 2 3 4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 21 22 23 24 25 26 27
En substance, nous les humains ne commençant pas par "0" est une chose CULTURELLE . Ce n'est pas rationnel.
Avec les tableaux à base zéro, vous pouvez utiliser un entier non signé comme index, puis vous n'avez pas à tester l'index hors plage sur la limite inférieure. par exemple:
int GetValue(unsigned index)
{
ASSERT(index < arraySize);
return(array[index];
}