L’index négatif pour l’opérateur est-il bien défini?

Je sais que ce serait un très mauvais style de codage, mais le code suivant fonctionne parfaitement sur ma machine. Mais le comportement est-il bien défini? Portable?

int main() { int *p = new int[3]; int *q = &p[2]; q[-1] = 41; std::cout << p[1]; delete[] p; } 

Ceci est bien défini, à la fois syntaxiquement et sémantiquement.

[expr.sub] / 1 (N3337):

L’expression E1[E2] est identique (par définition) à *((E1)+(E2)) .

Donc, votre expression est la même que *(q-1) = 41; , est donc syntaxiquement valide.

[expr.add] / 5 (N3337)

Lorsqu’une expression ayant un type intégral est ajoutée ou soustraite à un pointeur, le résultat a le type de l’opérande du pointeur. Si l’opérande pointe sur un élément d’un object 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 sorte que la différence entre les indices des éléments résultants et originaux soit égale à l’expression intégrale.

Puisque q pointe vers un élément d’un object tableau d’une taille valide pour votre expression intégrale, il est sémantiquement valide.

Oui, c’est bien défini. L’ operator[] intégré operator[] est défini en termes d’arithmétique de pointeur. Ce:

 p[N] 

p est un pointeur et N est un entier, est équivalent à ceci:

 *(p + N) 

Un résultat intéressant de ceci est que ceci:

 N[p] 

est également équivalent, car l’addition est commutative.

Selon le standard C ++ (5.2.1 Subscriptioning)

1 Une expression postfixe suivie d’une expression entre crochets est une expression postfixée. L’une des expressions doit avoir le type “array of T” ou “pointer to T” et l’autre doit avoir une énumération non découpée ou un type intégral . Le résultat est de type «T». Le type «T» doit être un type d’object complètement défini65. L’expression E1 [E2] est identique (par définition) à * ((E1) + (E2)) …

Vous pouvez donc utiliser n’importe quel type entier, y compris le type int et les valeurs négatives correspondantes, à condition que le résultat de l’expression *((E1)+(E2)) soit bien formé.

Tenez compte du fait que pour les types définis par l’utilisateur, vous pouvez utiliser une brace-init-list comme index. Par exemple

 #include  class Point { public: Point( int x, int y ) : x( x ), y( y ) {} int x, y; }; class Circle { public: Circle( unsigned int r ) : r( r ) {} Circle & operator []( Point p ) { std::cout << "Drawing a circle at ( " << px << ", " << py << " )\n"; return *this; } unsigned int r; }; int main() { Circle circle( 10 ); circle[ { 0, 0 } ]; } 

La sortie du programme est

 Drawing a circle at ( 0, 0 ) 

L’opérateur d’index x[idx] est égal à (*(x +idx)) et yes idx peut être négatif. Toutefois, vous devez vous assurer que le pointeur déréférencé indiquait une adresse mémoire valide.

Notez que nous pouvons le réécrire de plusieurs manières (comme l’algèbre).

 x[idx] = (*(x +idx)) = (*(idx + x)) = idx[x] 

C’est très bien tant que vous n’essayez pas de déréférencer un pointeur en dehors des limites du tableau pointé par p .

En outre, vous pouvez définir le pointeur q sur n’importe quel élément du tableau et sur un élément situé après le tableau. (N’essayez cependant pas de déréférencer un élément après la fin du siège.)

N’oubliez pas de delete[] p; à la fin de votre fonction.

Il est parfaitement sûr et portable. Vous utilisez simplement l’arithmétique de pointeur pour adresser la mémoire allouée par le new opérateur.

Votre code est équivalent à:

 int* p = new int[3]; int* q = p + 2; *(q-1) = 41;