Avec les tableaux, pourquoi se fait-il qu'un a [5] == 5 [a]?


1622

Comme le souligne Joel dans le podcast Stack Overflow # 34 , en langage de programmation C (alias: K & R), il est fait mention de cette propriété des tableaux en C:a[5] == 5[a]

Joel dit que c'est à cause de l'arithmétique des pointeurs mais je ne comprends toujours pas. Pourquoia[5] == 5[a] ?


48
quelque chose comme un [+] fonctionnerait-il aussi comme * (a ++) OU * (++ a)?
Egon

45
@Egon: C'est très créatif, mais ce n'est malheureusement pas ainsi que fonctionnent les compilateurs. Le compilateur interprète a[1]comme une série de jetons, pas de chaînes: * ({emplacement entier de} un {opérateur} + {entier} 1) est identique à * ({entier} 1 {opérateur} + {emplacement entier de} a) mais n'est pas la même chose que * ({emplacement entier} d'un {opérateur} + {opérateur} +)
Dinah

11
Une variation composée intéressante à ce sujet est illustrée dans l' accès au tableau illogique , où vous avez char bar[]; int foo[];et foo[i][bar]est utilisé comme expression.
Jonathan Leffler

5
@EldritchConundrum, pourquoi pensez-vous que le compilateur ne peut pas vérifier que la partie gauche est un pointeur? Oui il peut. Il est vrai que a[b]= *(a + b)pour tout donné aet b, mais c'était le libre choix des concepteurs de langage pour +être défini commutatif pour tous les types. Rien ne pouvait les empêcher d'interdire i + pen autorisant p + i.
ach

13
@Andrey One s'attend généralement +à être commutatif, alors peut-être que le vrai problème est de choisir des opérations de pointeur ressemblant à de l'arithmétique, au lieu de concevoir un opérateur de décalage séparé.
Eldritch Conundrum

Réponses:


1926

La norme C définit l' []opérateur comme suit:

a[b] == *(a + b)

Évaluera donc a[5]:

*(a + 5)

et 5[a]évaluera:

*(5 + a)

aest un pointeur sur le premier élément du tableau. a[5]est la valeur qui est éloignée de 5 élémentsa , ce qui est le même que *(a + 5), et des mathématiques du primaire, nous savons que ceux-ci sont égaux (l'addition est commutative ).


325
Je me demande si ce n'est pas plus comme * ((5 * sizeof (a)) + a). Excellente explication cependant.
John MacIntyre

92
@Dinah: Du point de vue du compilateur C, vous avez raison. Aucune taille n'est nécessaire et les expressions que j'ai mentionnées sont les mêmes. Cependant, le compilateur prendra en compte sizeof lors de la production du code machine. Si a est un tableau int, a[5]sera compilé en quelque chose comme mov eax, [ebx+20]au lieu de[ebx+5]
Mehrdad Afshari

12
@Dinah: A est une adresse, disons 0x1230. Si a était dans un tableau int de 32 bits, alors un [0] est à 0x1230, un [1] est à 0x1234, un [2] à 0x1238 ... un [5] à x1244 etc. Si nous ajoutons simplement 5 à 0x1230, nous obtenons 0x1235, ce qui est faux.
James Curran

36
@ sr105: C'est un cas particulier pour l'opérateur +, où l'un des opérandes est un pointeur et l'autre un entier. La norme indique que le résultat sera du type du pointeur. Le compilateur / doit être / assez intelligent.
BAI

48
"des mathématiques de l'école primaire, nous savons que celles-ci sont égales" - je comprends que vous simplifiez, mais je suis avec ceux qui pensent que c'est trop simplifier. Ce n'est pas élémentaire *(10 + (int *)13) != *((int *)10 + 13). En d'autres termes, il se passe plus ici que l'arithmétique au primaire. La commutativité repose de manière critique sur le compilateur qui reconnaît quel opérande est un pointeur (et à quelle taille d'objet). Pour le dire autrement (1 apple + 2 oranges) = (2 oranges + 1 apple), mais (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH

288

Parce que l'accès aux tableaux est défini en termes de pointeurs. a[i]est défini comme signifiant *(a + i), qui est commutatif.


42
Les tableaux ne sont pas définis en termes de pointeurs, mais leur accès l' est.
Courses de légèreté en orbite

5
J'ajouterais "donc il est égal à *(i + a), ce qui peut s'écrire i[a]".
Jim Balter

4
Je vous suggère d'inclure la citation de la norme, qui est la suivante: 6.5.2.1: 2 Une expression suffixe suivie d'une expression entre crochets [] est une désignation en indice d'un élément d'un objet tableau. La définition de l'opérateur d'indice [] est que E1 [E2] est identique à (* ((E1) + (E2))). En raison des règles de conversion qui s'appliquent à l'opérateur binaire +, si E1 est un objet tableau (de manière équivalente, un pointeur vers l'élément initial d'un objet tableau) et E2 est un entier, E1 [E2] désigne l'élément E2-ème de E1 (comptage à partir de zéro).
Vality

Pour être plus correct: les tableaux se désintègrent en pointeurs lorsque vous y accédez.
12431234123412341234123

Nitpick: Cela n'a pas de sens de dire que " *(a + i)est commutatif". Cependant, *(a + i) = *(i + a) = i[a]parce que l' addition est commutative.
Andreas Rejbrand

231

Je pense que quelque chose manque aux autres réponses.

Oui, p[i]est par définition équivalent à *(p+i), qui (parce que l'addition est commutative) est équivalent à *(i+p), qui (encore une fois, par la définition de l' []opérateur) est équivalent à i[p].

(Et dans array[i], le nom du tableau est implicitement converti en un pointeur vers le premier élément du tableau.)

Mais la commutativité de l'addition n'est pas si évidente dans ce cas.

Lorsque les deux opérandes sont du même type, ou même de différents types numériques qui sont promus à un type commun, commutativité est parfaitement logique: x + y == y + x.

Mais dans ce cas, nous parlons spécifiquement de l'arithmétique des pointeurs, où un opérande est un pointeur et l'autre est un entier. (Entier + entier est une opération différente et pointeur + pointeur est un non-sens.)

La description de l' +opérateur par la norme C ( N1570 6.5.6) dit:

De plus, soit les deux opérandes doivent avoir un type arithmétique, soit un opérande doit être un pointeur vers un type d'objet complet et l'autre doit avoir un type entier.

Il aurait tout aussi bien pu dire:

De plus, soit les deux opérandes doivent être de type arithmétique, soit l' opérande de gauche doit être un pointeur vers un type d'objet complet et l' opérande de droite doit avoir un type entier.

auquel cas les deux i + pet i[p]seraient illégaux.

En termes C ++, nous avons vraiment deux ensembles d' +opérateurs surchargés , qui peuvent être décrits de manière générale comme:

pointer operator+(pointer p, integer i);

et

pointer operator+(integer i, pointer p);

dont seul le premier est vraiment nécessaire.

Alors pourquoi est-ce ainsi?

C ++ a hérité de cette définition de C, qui l'a obtenue de B (la commutativité de l'indexation de tableaux est explicitement mentionnée dans la référence des utilisateurs de 1972 à B ), qui l'a obtenue de BCPL (manuel daté de 1967), qui pourrait bien l'avoir obtenue de même langues antérieures (CPL? Algol?).

Ainsi, l'idée que l'indexation de tableaux est définie en termes d'addition, et que l'addition, même d'un pointeur et d'un entier, est commutative, remonte à plusieurs décennies, aux langages des ancêtres de C.

Ces langages étaient beaucoup moins fortement typés que le C moderne. En particulier, la distinction entre pointeurs et entiers a souvent été ignorée. (Les premiers programmeurs C utilisaient parfois des pointeurs comme des entiers non signés, avant que le unsignedmot - clé ne soit ajouté au langage.) Ainsi, l'idée de rendre l'addition non commutative parce que les opérandes sont de types différents n'aurait probablement pas surgi pour les concepteurs de ces langages. Si un utilisateur voulait ajouter deux "choses", que ces "choses" soient des entiers, des pointeurs ou autre chose, ce n'était pas à la langue de l'empêcher.

Et au fil des ans, toute modification de cette règle aurait violé le code existant (bien que la norme ANSI C de 1989 ait pu être une bonne opportunité).

Changer C et / ou C ++ pour exiger de placer le pointeur à gauche et l'entier à droite pourrait casser du code existant, mais il n'y aurait aucune perte de puissance expressive réelle.

Nous avons donc maintenant arr[3]et 3[arr]signifiant exactement la même chose, bien que cette dernière forme ne devrait jamais apparaître en dehors de l' IOCCC .


12
Description fantastique de cette propriété. D'un point de vue élevé, je pense que 3[arr]c'est un artefact intéressant mais devrait rarement, voire jamais, être utilisé. La réponse acceptée à cette question (< stackoverflow.com/q/1390365/356> ) que j'ai posée il y a quelque temps a changé ma façon de penser la syntaxe. Bien qu'il n'y ait souvent techniquement pas une bonne et une mauvaise façon de faire ces choses, ces types de fonctionnalités vous font penser d'une manière distincte des détails de mise en œuvre. Il y a des avantages à cette façon de penser différente qui est en partie perdue lorsque vous fixez les détails de la mise en œuvre.
Dinah

3
L'addition est commutative. Pour la norme C, la définir autrement serait étrange. C'est pourquoi il ne pourrait pas tout aussi facilement dire "Pour l'addition, les deux opérandes doivent avoir un type arithmétique, ou l'opérande de gauche doit être un pointeur vers un type d'objet complet et l'opérande de droite doit avoir un type entier." - Cela n'aurait pas de sens pour la plupart des gens qui ajoutent des choses.
iheanyi

9
@iheanyi: L'addition est généralement commutative - et elle prend généralement deux opérandes du même type. L'ajout de pointeur vous permet d'ajouter un pointeur et un entier, mais pas deux pointeurs. À mon humble avis, c'est déjà un cas spécial suffisamment étrange pour que le pointeur soit l'opérande gauche ne serait pas un fardeau important. (Certaines langues utilisent "+" pour la concaténation de chaînes; ce n'est certainement pas commutatif.)
Keith Thompson

3
@supercat, c'est encore pire. Cela signifierait que parfois x + 1! = 1 + x. Cela violerait complètement la propriété associative de l'addition.
iheanyi

3
@iheanyi: Je pense que vous vouliez parler de propriété commutative; l'addition n'est déjà pas associative, car sur la plupart des implémentations (1LL + 1U) -2! = 1LL + (1U-2). En effet, le changement rendrait certaines situations associatives qui ne le sont pas actuellement, par exemple 3U + (UINT_MAX-2L) serait égal à (3U + UINT_MAX) -2. Ce qui serait le mieux, cependant, est que le langage ait ajouté de nouveaux types distincts pour les entiers promotables et les "cercles" algébriques, de sorte que l'ajout de 2 à un ring16_tqui contient 65535 produirait un ring16_tavec une valeur 1, indépendamment de la taille deint .
supercat

196

Et bien sûr

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

La principale raison en était que dans les années 70, lorsque C a été conçu, les ordinateurs n'avaient pas beaucoup de mémoire (64 Ko, c'était beaucoup), donc le compilateur C ne faisait pas beaucoup de vérification de la syntaxe. Par conséquent, " X[Y]" a été traduit aveuglément en " *(X+Y)"

Cela explique également les syntaxes " +=" et " ++". Tout sous la forme " A = B + C" avait la même forme compilée. Mais, si B était le même objet que A, alors une optimisation au niveau de l'assemblage était disponible. Mais le compilateur n'était pas assez brillant pour le reconnaître, donc le développeur a dû ( A += C). De même, si Cc'était le cas 1, une optimisation de niveau d'assemblage différente était disponible, et encore une fois, le développeur devait la rendre explicite, car le compilateur ne l'a pas reconnue. (Plus récemment, les compilateurs le font, donc ces syntaxes sont largement inutiles de nos jours)


127
En fait, cela équivaut à faux; le premier terme "ABCD" [2] == 2 ["ABCD"] est évalué à vrai, ou 1, et 1! = 'C': D
Jonathan Leffler

8
@Jonathan: la même ambiguïté a conduit à l'édition du titre original de ce post. Sommes-nous l'équivalence mathématique, la syntaxe du code ou le pseudo-code? Je soutiens l'équivalence mathématique mais puisque nous parlons de code, nous ne pouvons pas échapper à ce que nous voyons tout en termes de syntaxe de code.
Dinah

19
N'est-ce pas un mythe? Je veux dire que les opérateurs + = et ++ ont été créés pour simplifier pour le compilateur? Un peu de code devient plus clair avec eux, et c'est une syntaxe utile à avoir, peu importe ce que le compilateur en fait.
Thomas Padron-McCarthy

6
+ = et ++ présente un autre avantage significatif. si le côté gauche change une variable pendant l'évaluation, le changement ne sera effectué qu'une seule fois. a = a + ...; le fera deux fois.
Johannes Schaub - litb

8
Non - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Déréférencer une chaîne vous donne un caractère, pas une sous
MSalters

55

Personne ne semble avoir mentionné le problème de Dinah avec sizeof :

Vous ne pouvez ajouter qu'un entier à un pointeur, vous ne pouvez pas ajouter deux pointeurs ensemble. De cette façon, lors de l'ajout d'un pointeur à un entier ou d'un entier à un pointeur, le compilateur sait toujours quel bit a une taille qui doit être prise en compte.


1
Il y a une conversation assez exhaustive à ce sujet dans les commentaires de la réponse acceptée. J'ai référencé ladite conversation dans l'édition à la question d'origine, mais je n'ai pas directement répondu à votre préoccupation très valable de sizeof. Je ne sais pas comment faire cela au mieux dans SO. Dois-je faire un autre montage à l'origine. question?
Dinah

49

Pour répondre à la question littéralement. Il n'est pas toujours vrai quex == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

impressions

false

27
En fait, un "nan" n'est pas égal à lui-même: cout << (a[5] == a[5] ? "true" : "false") << endl;est false.
TrueY

9
@ TrueY: Il l'a déclaré spécifiquement pour le cas NaN (et spécifiquement ce x == xn'est pas toujours vrai). Je pense que c'était son intention. Il est donc techniquement correct (et peut-être, comme on dit, le meilleur type de correct!).
Tim Čas

4
La question concerne C, votre code n'est pas du code C. Il y a aussi un NANin <math.h>, ce qui est mieux que 0.0/0.0, car 0.0/0.0UB __STDC_IEC_559__n'est pas défini (la plupart des implémentations ne définissent pas __STDC_IEC_559__, mais sur la plupart des implémentations 0.0/0.0fonctionneront toujours)
12431234123412341234123

26

Je viens de découvrir que cette syntaxe laide pourrait être "utile", ou du moins très amusante à jouer avec un tableau d'index qui fait référence à des positions dans le même tableau. Il peut remplacer les crochets imbriqués et rendre le code plus lisible!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Bien sûr, je suis sûr qu'il n'y a pas de cas d'utilisation pour cela dans du vrai code, mais je l'ai quand même trouvé intéressant :)


Quand vous voyez, i[a][a][a]vous pensez que i est soit un pointeur vers un tableau ou un tableau d'un pointeur vers un tableau ou un tableau ... et aest un index. Quand vous voyez a[a[a[i]]], vous pensez que a est un pointeur vers un tableau ou un tableau et iest un index.
12431234123412341234123

1
Hou la la! C'est très cool d'utiliser cette fonctionnalité "stupide". Pourrait être utile en concours algorithmique dans certains problèmes))
Serge Breusov

26

Belles questions / réponses.

Je veux juste souligner que les pointeurs et les tableaux C ne sont pas les mêmes , bien que dans ce cas la différence ne soit pas essentielle.

Considérez les déclarations suivantes:

int a[10];
int* p = a;

Dans a.out, le symbole se atrouve à une adresse qui est le début du tableau, et le symbole se ptrouve à une adresse où un pointeur est stocké, et la valeur du pointeur à cet emplacement de mémoire est le début du tableau.


2
Non, techniquement, ce ne sont pas les mêmes. Si vous définissez un b comme int * const et le faites pointer vers un tableau, il s'agit toujours d'un pointeur, ce qui signifie que dans la table des symboles, b fait référence à un emplacement de mémoire qui stocke une adresse, qui à son tour pointe vers l'emplacement du tableau. .
PolyThinker

4
Très bon point. Je me souviens avoir eu un bug très désagréable quand j'ai défini un symbole global comme char s [100] dans un module, le déclarer comme char externe *; dans un autre module. Après avoir tout lié, le programme s'est comporté de façon très étrange. Parce que le module utilisant la déclaration extern utilisait les octets initiaux du tableau comme pointeur sur char.
Giorgio

1
À l'origine, dans le grand-parent BCPL de C, un tableau était un pointeur. Autrement dit, ce que vous avez obtenu lorsque vous avez écrit (j'ai translittéré en C) int a[10]était un pointeur appelé «a», qui indiquait suffisamment de mémoire pour 10 entiers, ailleurs. Ainsi, a + i et j + i avaient la même forme: ajouter le contenu de quelques emplacements mémoire. En fait, je pense que BCPL était sans type, donc ils étaient identiques. Et la mise à l'échelle du type de taille ne s'appliquait pas, car BCPL était purement orienté sur les mots (sur les machines à adressage de mots également).
Dave

Je pense que la meilleure façon de comprendre la différence est de comparer int*p = a;à int b = 5; Dans ce dernier, "b" et "5" sont tous deux des entiers, mais "b" est une variable, tandis que "5" est une valeur fixe. De même, "p" et "a" sont les deux adresses d'un caractère, mais "a" est une valeur fixe.
James Curran

20

Pour les pointeurs en C, nous avons

a[5] == *(a + 5)

et aussi

5[a] == *(5 + a)

Il est donc vrai que a[5] == 5[a].


15

Pas une réponse, mais juste quelques pistes de réflexion. Si la classe a un opérateur d'index / indice surchargé, l'expression 0[x]ne fonctionnera pas:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Puisque nous n'avons pas accès à la classe int , cela ne peut pas être fait:

class int
{
   int operator[](const Sub&);
};

2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
Ben Voigt

1
Avez-vous essayé de le compiler? Il existe un ensemble d'opérateurs qui ne peuvent pas être implémentés en dehors de la classe (c'est-à-dire en tant que fonctions non statiques)!
Ajay

3
Oups, vous avez raison. " operator[]doit être une fonction membre non statique avec exactement un paramètre." Je connaissais cette restriction operator=, je ne pensais pas qu'elle s'appliquait [].
Ben Voigt

1
Bien sûr, si vous changez la définition d' []opérateur, ce ne sera plus jamais équivalent ... si a[b]est égal à *(a + b)et vous changez cela, vous devrez également surcharger int::operator[](const Sub&);et ce intn'est pas une classe ...
Luis Colorado

7
Ce ... n'est pas ... C.
MD XF

11

Il a une très bonne explication dans A TUTORIAL ON POINTERS AND ARRAYS IN C de Ted Jensen.

Ted Jensen l'a expliqué comme suit:

En fait, cela est vrai, c'est-à-dire que partout où l'on écrit, a[i]il peut être remplacé *(a + i) sans problème. En fait, le compilateur créera le même code dans les deux cas. Ainsi, nous voyons que l'arithmétique des pointeurs est la même chose que l'indexation des tableaux. L'une ou l'autre syntaxe produit le même résultat.

Cela ne veut PAS dire que les pointeurs et les tableaux sont la même chose, ils ne le sont pas. Nous disons seulement que pour identifier un élément donné d'un tableau, nous avons le choix entre deux syntaxes, l'une utilisant l'indexation des tableaux et l'autre utilisant l'arithmétique des pointeurs, qui donnent des résultats identiques.

Maintenant, en regardant cette dernière expression, en partie .. (a + i), est un simple ajout en utilisant l'opérateur + et les règles de C indiquent qu'une telle expression est commutative. C'est-à-dire que (a + i) est identique à (i + a). Ainsi, nous pourrions écrire *(i + a)aussi facilement que *(a + i). Mais *(i + a)aurait pu venir i[a]! De tout cela vient la curieuse vérité que si:

char a[20];

l'écriture

a[3] = 'x';

revient à écrire

3[a] = 'x';

4
a + i n'est PAS un simple ajout, car c'est une arithmétique de pointeur. si la taille de l'élément de a est 1 (char), alors oui, c'est comme entier +. Mais si c'est (par exemple) un entier, alors il pourrait être équivalent à un + 4 * i.
Alex Brown

@AlexBrown Oui, c'est de l'arithmétique des pointeurs, c'est exactement pourquoi votre dernière phrase est erronée, à moins que vous ne lanciez d'abord 'a' pour être un (char *) (en supposant qu'un int soit 4 caractères). Je ne comprends vraiment pas pourquoi tant de gens sont accrochés au résultat de la valeur réelle de l'arithmétique des pointeurs. Le but de l'arithmétique du pointeur est d'abstraire les valeurs sous-jacentes du pointeur et de laisser le programmeur penser aux objets manipulés plutôt qu'aux valeurs d'adresse.
jschultz410

8

Je sais qu'on a répondu à la question, mais je n'ai pas pu résister à partager cette explication.

Je me souviens des principes de conception du compilateur, supposons aqu'un inttableau ait une taille de int2 octets et que l'adresse de base asoit 1000.

Comment ça a[5]va marcher ->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Donc,

De même, lorsque le code c est décomposé en code à 3 adresses, 5[a]deviendra ->

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Donc , fondamentalement , les deux déclarations pointent vers le même emplacement en mémoire et , par conséquent, a[5] = 5[a].

Cette explication est également la raison pour laquelle les indices négatifs dans les tableaux fonctionnent en C.

c'est-à-dire que si j'y accède, a[-5]cela me donnera

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Il me rendra l'objet à l'emplacement 990.


6

Dans les tableaux C , arr[3]et 3[arr]sont les mêmes, et leurs notations de pointeur équivalentes sont *(arr + 3)à *(3 + arr). Mais au contraire [arr]3ou [3]arrn'est pas correct et entraînera une erreur de syntaxe, car (arr + 3)*et (3 + arr)*ne sont pas des expressions valides. La raison en est que l'opérateur de déréférencement doit être placé avant l'adresse fournie par l'expression, et non après l'adresse.


6

dans le compilateur c

a[i]
i[a]
*(a+i)

sont différentes façons de faire référence à un élément d'un tableau! (PAS DU tout bizarre)


5

Un peu d'histoire maintenant. Entre autres langages, BCPL a eu une influence assez importante sur le développement précoce de C. Si vous avez déclaré un tableau dans BCPL avec quelque chose comme:

let V = vec 10

qui allouait en fait 11 mots de mémoire, pas 10. Typiquement, V était le premier et contenait l'adresse du mot immédiatement suivant. Donc, contrairement à C, nommer V est allé à cet endroit et a pris l'adresse du zéro élément du tableau. Par conséquent, l'indirection de tableau dans BCPL, exprimée en

let J = V!5

vraiment à faire J = !(V + 5)(en utilisant la syntaxe BCPL) car il était nécessaire de récupérer V pour obtenir l'adresse de base du tableau. Ainsi V!5et5!V étaient synonymes. Comme observation anecdotique, le WAFL (Warwick Functional Language) a été écrit en BCPL, et au mieux de ma mémoire avait tendance à utiliser cette dernière syntaxe plutôt que la première pour accéder aux nœuds utilisés comme stockage de données. Certes, cela remonte à environ 35 à 40 ans, donc ma mémoire est un peu rouillée. :)

L'innovation de supprimer le mot de stockage supplémentaire et de demander au compilateur d'insérer l'adresse de base de la baie quand il a été nommé est venue plus tard. Selon le journal d'histoire C, cela s'est produit à peu près au moment où les structures ont été ajoutées à C.

Notez que !dans BCPL était à la fois un opérateur de préfixe unaire et un opérateur d'infixe binaire, dans les deux cas faisant de l'indirection. juste que la forme binaire incluait un ajout des deux opérandes avant de faire l'indirection. Étant donné la nature orientée mot de BCPL (et B), cela avait en fait beaucoup de sens. La restriction du "pointeur et entier" a été rendue nécessaire en C quand il a gagné des types de données, et sizeofest devenue une chose.


1

Eh bien, c'est une fonctionnalité qui n'est possible qu'en raison de la prise en charge des langues.

Le compilateur interprète a[i]comme *(a+i)et l'expression est 5[a]évaluée *(5+a). Puisque l'addition est commutative, il s'avère que les deux sont égaux. Par conséquent, l'expression est évaluée à true.


Bien que redondant, il est clair, concis et court.
Bill K

0

En C

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

Le pointeur est une "variable"

le nom du tableau est un "mnémonique" ou un "synonyme"

p++;est valide mais a++n'est pas valide

a[2] est égal à 2 [a] parce que le fonctionnement interne sur les deux est

"Arithmétique du pointeur" calculée en interne comme

*(a+3) équivaut à *(3+a)


-4

types de pointeurs

1) pointeur vers les données

int *ptr;

2) pointeur const vers les données

int const *ptr;

3) pointeur const vers les données const

int const *const ptr;

et les tableaux sont de type (2) de notre liste
Lorsque vous définissez un tableau à la fois, une adresse est initialisée dans ce pointeur
Comme nous savons que nous ne pouvons pas changer ou modifier la valeur de const dans notre programme car cela déclenche une ERREUR lors de la compilation temps

La principale différence que j'ai trouvée est ...

On peut réinitialiser le pointeur par une adresse mais pas le même cas avec un tableau.

======
et revenons à votre question ...
a[5]n'est rien mais *(a + 5)
vous pouvez facilement comprendre en
a - contenant l'adresse (les gens l'appellent comme adresse de base) tout comme un type de pointeur (2) dans notre liste
[]- cet opérateur peut être remplaçable par un pointeur *.

donc finalement ...

a[5] == *(a +5) == *(5 + a) == 5[a] 
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.