Avec les tableaux, pourquoi est-ce le cas que a[5] == 5[a]?

comme Joel le souligne dans podcast de débordement de pile # 34 , dans C Langage de programmation (alias: K & R), Il est fait mention de cette propriété de tableaux dans C: a[5] == 5[a]

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

1449
demandé sur Lundin 2008-12-19 20:01:33
la source

17 ответов

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

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

donc a[5] va évaluer à:

*(a + 5)

et 5[a] seront évalués à:

*(5 + a)

a est un pointeur vers le premier élément du tableau. a[5] est la valeur 5 éléments plus de a , qui est le même que *(a + 5) , et de l'école élémentaire mathématiques nous savons que ceux sont égaux (ajout est commutative ).

1723
répondu Mehrdad Afshari 2016-11-30 13:47:52
la source

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

273
répondu David Thornley 2011-05-13 17:47:10
la source

je pense que les autres réponses passent à côté de quelque chose.

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

(Et 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.

quand les deux opérandes sont du même type, ou même de différents types numériques qui sont promus à un type commun, la commutativité a un sens parfait: x + y == y + x .

mais dans ce cas nous parlons spécifiquement de l'arithmétique pointeur, où un opérande est un pointeur et l'autre est un entier. (Entier + entier est une opération différente, et pointer + pointer est absurdité.)

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

en outre, soit les deux opérandes doivent être de type arithmétique, operand doit être un pointeur vers un type d'objet complet et l'autre doit être de type entier.

Il aurait tout aussi bien pu dire:

à ajouter, soit les deux opérandes doivent être de type arithmétique, soit la gauche l'opérande doit être un pointeur vers un objet de type et de la opérande de droite doit être de type entier.

, auquel cas i + p et i[p] seraient tous deux illégaux.

en termes de C++, nous avons vraiment deux ensembles d'opérateurs + surchargés, qui peuvent être vaguement décrits comme:

pointer operator+(pointer p, integer i);

et

pointer operator+(integer i, pointer p);

dont seule la première est vraiment nécessaire.

alors pourquoi est-ce ainsi?

C++ a hérité cette définition de C, qui L'a obtenu de B( la commutativité de l'indexation de tableau est explicitement mentionnée dans le 1972 référence de L'utilisateur à B ), qui l'a obtenu de BCPL (manuel de 1967), qui peut bien l'avoir obtenu 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 à de nombreuses décennies, aux langues ancêtre de C.

ces langues étaient beaucoup moins fortement typées que les langues modernes. En particulier, la distinction entre les pointeurs et les entiers a été souvent ignoré. (Les premiers programmeurs C ont parfois utilisé des pointeurs comme des entiers non signés, avant la unsigned mot-clé a été ajouté à la langue. Ainsi l'idée de rendre l'addition non commutative parce que les opérandes sont de différents types ne serait probablement pas venue à l'esprit des 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 au langage de l'empêcher.

et au fil des ans, tout changement apporté à cette règle aurait enfreint le code existant (bien que la norme ANSI C de 1989 aurait pu être une bonne occasion).

changer C et / ou C++ pour exiger de mettre le pointeur sur la gauche et l'entier sur la droite pourrait briser quelque code existant, mais il n'y aurait pas de perte de puissance d'expression réelle.

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

190
répondu Keith Thompson 2016-01-28 23:48:59
la source

et, bien sûr

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

la raison principale pour cela était que dans les années 70 quand C a été conçu, les ordinateurs n'avaient pas beaucoup de mémoire (64KB était beaucoup), donc le compilateur C n'a pas fait beaucoup de vérification de syntaxe. Par conséquent " X[Y] "a été plutôt aveuglément traduit en " *(X+Y) "

cela explique aussi les " += " et " ++ " syntaxes. Tout dans la forme " A = B + C " avaient la même forme compilée. Mais, si B était le même objet que A, puis une optimisation du niveau d'assemblage était disponible. Mais le compilateur n'était pas assez brillant pour le reconnaître, donc le développeur a dû le faire ( A += C ). De même , si C était 1 , une optimisation différente du niveau d'assemblage était disponible, et encore une fois le développeur a dû le rendre explicite, parce que le compilateur ne l'a pas reconnu. (Plus récemment compilateurs font, de sorte que ces syntaxes sont largement inutile ces jours-ci)

185
répondu James Curran 2017-07-28 20:51:58
la source

une chose que personne ne semble avoir mentionnée au sujet du problème de Dinah avec sizeof :

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

51
répondu user30364 2009-12-18 11:01:42
la source

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

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

imprime

false
47
répondu Peter Lawrey 2011-08-11 17:50:22
la source

Belle question/réponses.

juste pour souligner que les pointeurs C et les tableaux ne sont pas le même , bien que dans ce cas la différence n'est pas essentielle.

considérer les déclarations suivantes:

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

Dans un. , le symbole un est une adresse de début du tableau, et le symbole p est à une adresse où un pointeur est stocké, et la valeur du pointeur à l'emplacement de la mémoire est le début du tableau.

23
répondu PolyThinker 2008-12-20 11:16:20
la source

je viens de découvrir que cette vilaine syntaxe pourrait être" utile", ou au moins très amusante à jouer quand vous voulez traiter avec un tableau d'index qui se réfèrent à 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 tout à fait sûr qu'il n'y a pas de cas d'utilisation pour cela dans le code réel, mais je l'ai trouvé intéressant de toute façon:)

21
répondu Frédéric Terrazzoni 2012-06-10 23:50:54
la source

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].

17
répondu user1055604 2012-03-23 21:36:57
la source

ce n'est pas une réponse, mais juste de quoi réfléchir. Si la classe A l'opérateur index/subscript 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&);
};
14
répondu Ajay 2015-12-09 21:29:15
la source

il a très bonne explication dans un tutoriel sur les pointeurs et les tableaux en C par Ted Jensen.

Ted Jensen l'a expliqué comme suit:

en fait, c'est vrai, I. e partout où l'on écrit a[i] il peut être remplacé par *(a + i) sans aucun problème. En fait, le compilateur va créer le même code dans les deux cas. Ainsi, nous voyons ce pointeur l'arithmétique est la même chose que le tableau d'indexation. Soit la syntaxe produit le même résultat.

ce N'est pas dire que les pointeurs et les tableaux sont la même chose, ils ne le sont pas. Nous disons seulement cela pour identifier un élément d'un tableau, nous avons le choix de deux syntaxes, un en utilisant l'indexation de tableau et l'autre en utilisant l'arithmétique de pointeur, qui des résultats identiques.

Maintenant, en regardant ce dernier expression, en partie.. (a + i) , est un simple ajout en utilisant le + l'opérateur et les règles de C état que cette expression est commutative. C'est-à-dire (a + i) est identique à (i + a) . Ainsi, nous pouvons écrivez *(i + a) aussi facilement que *(a + i) . Mais *(i + a) aurait pu venir de i[a] ! De tout cela, vient le curieux vérité que si:

char a[20];

écriture

a[3] = 'x';

est la même chose que l'écriture

3[a] = 'x';
9
répondu A.s. Bhullar 2017-01-13 09:00:35
la source

je sais qu'on répond à la question, mais je n'ai pas pu m'empêcher de partager cette explication.

je me souviens des principes de la conception du compilateur, Supposons que a est un tableau int et que la taille de int est de 2 octets, & L'adresse de Base pour a est 1000.

comment a[5] fonctionnera - >

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

,

de même lorsque le code c est décomposé en 3-adresse code, 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 énoncés pointent vers le même endroit dans la mémoire et donc, a[5] = 5[a] .

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

c'est à dire si j'ai accès a[-5] il va me donner

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
répondu Ajinkya Patil 2016-11-29 04:18:48
la source

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

4
répondu Krishan 2013-12-17 15:22:08
la source

dans le compilateur c

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

sont des façons différentes de faire référence à un élément d'un tableau ! (NOT AT ALL WEIRD)

4
répondu AVIK DUTTA 2014-10-29 12:14:54
la source

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

pointeur est une "variable"

nom de tableau est un "mnémonique" ou "synonyme"

p++; est valide mais a++ est invalide."

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

"arithmétique de pointage" calculée à l'interne comme

*(a+3) = *(3+a)

0
répondu Jayghosh Wankar 2018-01-07 12:49:53
la source

Eh bien, c'est une caractéristique qui n'est possible que grâce au support de la langue.

le compilateur interprète a[i] comme *(a+i) et l'expression 5[a] comme *(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 .

0
répondu Harsha JK 2018-04-02 21:42:27
la source

types pointeur

1) pointeur vers les données

int *ptr;

2) const pointeur de données

int const *ptr;

3) const pointeur const données

int const *const ptr;

et les tableaux sont des types de (2) de notre liste

Lorsque vous définir un tableau à un moment adresse est initialiser dans ce pointeur

Comme nous l' sachez que nous ne pouvons pas changer ou modifier la valeur de const dans notre programme car il lance une" erreur 1519150920 au moment de la compilation

Le différence importante que j'ai trouvé est...

nous pouvons ré-initialiser le pointeur par une adresse mais pas le même cas avec un tableau.

======

et revenons à votre question...

[5] n'est rien, mais *(un + 5)

, vous pouvez comprendre facilement par

un contenant de l'adresse (les gens l'appellent comme adresse de base) comme un (2) type de pointeur dans la liste de nos

[]- cet opérateur peut être remplacé par un pointeur * .

enfin...

a[5] == *(a +5) == *(5 + a) == 5[a] 
-2
répondu Jeet Parikh 2018-07-13 10:34:11
la source

Autres questions sur pointers c arrays pointer-arithmetic