Les index de tableau négatifs sont-ils autorisés en C?

115

Je lisais juste du code et j'ai trouvé que la personne utilisait arr[-2]pour accéder au 2ème élément avant le arr, comme ceci:

|a|b|c|d|e|f|g|
       ^------------ arr[0]
         ^---------- arr[1]
   ^---------------- arr[-2]

Est-ce permis?

Je sais que arr[x]c'est la même chose *(arr + x). Ainsi arr[-2]est *(arr - 2), ce qui semble OK. Qu'est-ce que tu penses?

Bodacydo
la source

Réponses:

168

C'est correct. À partir de C99 §6.5.2.1 / 2:

La dé fi nition de l'opérateur indice [] est que E1 [E2] est identique à (* ((E1) + (E2))).

Il n'y a pas de magie. C'est une équivalence 1-1. Comme toujours lors du déréférencement d'un pointeur (*), vous devez vous assurer qu'il pointe vers une adresse valide.

Matthew Flaschen
la source
2
Notez également que vous n'avez pas besoin de déréférencer le pointeur pour obtenir UB. Seul le calcul somearray-2est indéfini sauf si le résultat est compris entre le début somearrayet 1 après sa fin.
RBerteig
34
Dans les livres plus anciens, ils []étaient référencés comme un sucre de syntaxe pour l'arithmétique des pointeurs. La façon préférée de confondre les débutants est d'écrire 1[arr]- au lieu de arr[1]- et de les regarder deviner ce que cela signifie.
Dummy00001
4
Que se passe-t-il sur les systèmes 64 bits (LP64) lorsque vous avez un index int 32 bits qui est négatif? L'index doit-il être promu en un entier signé 64 bits avant le calcul de l'adresse?
Paul R du
4
@Paul, du §6.5.6 / 8 (Opérateurs additifs), "Lorsqu'une expression de type entier est ajoutée ou soustraite d'un pointeur, le résultat a le type de l'opérande du pointeur. Si l'opérande du pointeur pointe vers un élément d'un objet tableau et que le tableau est suffisamment grand, le résultat pointe vers un élément décalé par rapport à l'élément d'origine de telle sorte que la différence des indices des éléments du tableau résultant et d'origine égale l'expression entière. " Je pense donc qu'il sera promu et qu'il ((E1)+(E2))s'agira d'un pointeur (64 bits) avec la valeur attendue.
Matthew Flaschen
@Matthew: merci pour cela - il semble que cela devrait fonctionner comme on pourrait raisonnablement s'y attendre.
Paul R
63

Ceci n'est valide que s'il arrs'agit d'un pointeur qui pointe vers le deuxième élément d'un tableau ou un élément ultérieur. Sinon, ce n'est pas valide, car vous accéderiez à la mémoire en dehors des limites du tableau. Donc, par exemple, ce serait faux:

int arr[10];

int x = arr[-2]; // invalid; out of range

Mais ce serait bien:

int arr[10];
int* p = &arr[2];

int x = p[-2]; // valid:  accesses arr[0]

Il est cependant inhabituel d'utiliser un indice négatif.

James McNellis
la source
Je n'irais pas jusqu'à dire que c'est invalide, juste potentiellement désordonné
Matt Joiner
13
@Matt: le code du premier exemple produit un comportement indéfini.
James McNellis
5
C'est invalide. Par le standard C, il a explicitement un comportement indéfini. D'autre part, si int arr[10];faisait partie d'une structure avec d'autres éléments avant elle, arr[-2]pourrait potentiellement être bien définie, et vous pourriez déterminer si elle est basée sur offsetof, etc.
R .. GitHub STOP AIDER ICE
4
Trouvé dans la section 5.3 de K&R, vers la fin: If one is sure that the elements exist, it is also possible to index backwards in an array; p[-1], p[-2], and so on are syntactically legal, and refer to the elements that immediately precede p[0]. Of course, it is illegal to refer to objects that are not within the array bounds.Pourtant, votre exemple est meilleur pour m'aider à le comprendre. Merci!
Qiang Xu
4
Désolé pour la nécromancie des fils, mais j'aime juste la façon dont K&R est ambigu sur ce que signifie «illégal». La dernière phrase donne l'impression que les accès hors limites génèrent une erreur de compilation. Ce livre est un poison pour les débutants.
Martin
12

Cela me convient. Ce serait un cas rare où vous en auriez légitimement besoin.

Matt Joiner
la source
9
Ce n'est pas si rare - c'est très utile par exemple dans le traitement d'image avec les opérateurs de quartier.
Paul R
J'avais juste besoin d'utiliser ceci parce que je crée un pool de mémoire avec une pile et un tas [structure / conception]. La pile grandit vers des adresses mémoire plus élevées, le tas grandit vers des adresses mémoire plus faibles. Rencontre au milieu.
JMI MADISON le
8

Ce qui était probablement qui arrpointait vers le milieu du tableau, faisant ainsi arr[-2]pointer vers quelque chose dans le tableau d'origine sans sortir des limites.

Igor Zevaka
la source
7

Je ne sais pas à quel point cela est fiable, mais je viens de lire la mise en garde suivante concernant les indices de tableau négatifs sur les systèmes 64 bits (probablement LP64): http://www.devx.com/tips/Tip/41349

L'auteur semble dire que les indices de tableau int 32 bits avec un adressage 64 bits peuvent entraîner de mauvais calculs d'adresse à moins que l'index de tableau ne soit explicitement promu à 64 bits (par exemple via un cast ptrdiff_t). J'ai en fait vu un bogue de sa nature avec la version PowerPC de gcc 4.1.0, mais je ne sais pas si c'est un bogue du compilateur (c.-à-d. Devrait fonctionner selon la norme C99) ou un comportement correct (c.-à-d. Que l'index a besoin d'un cast en 64 bits pour un comportement correct)?

Paul R
la source
3
Cela ressemble à un bogue du compilateur.
tbleher
2

Je sais que la question est répondue, mais je n'ai pas pu résister à partager cette explication.

Je me souviens des principes de conception du compilateur, supposons que a est un tableau int et que la taille de int est 2, & l'adresse de base pour a est 1000.

Comment a[5]fonctionnera ->

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

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

ie si j'y accède, a[-5]ça me donnera

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

Il me renverra l'objet à l'emplacement 990. Par cette logique, nous pouvons accéder aux index négatifs dans Array en C.

Ajinkya Patil
la source
2

À propos des raisons pour lesquelles quelqu'un voudrait utiliser des index négatifs, je les ai utilisés dans deux contextes:

  1. Avoir une table de nombres combinatoires qui vous indique comb [1] [- 1] = 0; vous pouvez toujours vérifier les index avant d'accéder à la table, mais de cette façon le code semble plus propre et s'exécute plus rapidement.

  2. Mettre un centinel au début d'une table. Par exemple, vous souhaitez utiliser quelque chose comme

     while (x < a[i]) i--;

mais alors vous devriez également vérifier que ic'est positif.
Solution: faites en sorte que ce a[-1]soit -DBLE_MAXainsi, ce x&lt;a[-1]sera toujours faux.

Santiago Egido Arteaga
la source
0
#include <stdio.h>

int main() // negative index
{ 
    int i = 1, a[5] = {10, 20, 30, 40, 50};
    int* mid = &a[5]; //legal;address,not element there
    for(; i < 6; ++i)
    printf(" mid[ %d ] = %d;", -i, mid[-i]);
}
Rathinavelu Muthaliar
la source
1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la raison et / ou la manière dont ce code répond à la question améliore sa valeur à long terme.
β.εηοιτ.βε
Python groovy ... les avoir. Un cas d'utilisation simple est que l'on peut accéder au dernier élément d'un tableau sans connaître la taille du tableau, une exigence très réelle dans de nombreuses situations de projet. De nombreux DSL en bénéficient également.
Rathinavelu Muthaliar