2015-08-18 23 views
40

So che sarebbe pessimo stile di codifica, ma il codice seguente funziona perfettamente sulla mia macchina. Ma il comportamento è ben definito? Portatile?L'indice negativo per l'operatore [] è ben definito?

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

Just an sulla risposta tovagliolo a memoria, ma credo che foo [x] è definito nello standard come esattamente equivalente a * (foo + x) dove x è un tipo integrale e foo è un puntatore quindi penso che sarebbe legale in qualsiasi posto sia il più tardi. – Vality

+7

Non farlo in C++. Se vedo 'thing [-1]' penso "oh, posso leggere bene l'ultimo elemento" - come Python, non "è la cosa prima di qualunque cosa ciò faccia" –

+16

@AlecTeal che non è davvero un buon motivo per non consentire Questo. Se abbiamo respinto ogni espressione che ingannevolmente somigliava a una forma di un'altra lingua popolare, non sarebbe rimasto nulla. Non saremmo neanche in grado di scrivere le dichiarazioni di IF perché assomigliano molto all'IF aritmetico di Fortan II. –

risposta

42

Questo è ben definito, sia sintatticamente che semanticamente.

[expr.sub]/1 (N3337):

L'espressione E1[E2] è identica (per definizione) per *((E1)+(E2)).

Quindi la tua espressione è la stessa di *(q-1) = 41;, quindi è sintatticamente valida.

[expr.add]/5 (N3337)

Quando si aggiunge un'espressione che contiene tipo integrale o sottratto da un puntatore, il risultato ha il tipo del puntatore operando. Se l'operando puntatore punta a un elemento di un oggetto array e l'array è sufficientemente grande, il risultato punta a un offset dell'elemento dall'elemento originale in modo tale che la differenza tra gli indici degli elementi dell'array risultante e originale sia uguale all'espressione integrale.

Dal q punti a un elemento di un oggetto matrice di una dimensione valida per l'espressione integrale, è semanticamente valido.

+0

Che dire del '*'? – immibis

+1

@immibis Ho deciso di lasciarlo fuori, poiché questa domanda riguarda la validità dell'indice negativo; l'indirezione è separata, sebbene sia ovviamente correlata al risultato, che è definito nella seconda citazione. Se aggiungere informazioni sull'indirizzamento indiretto aiuterebbe l'OP, sarei felice di aggiungerlo, ma non credo sia necessario. – TartanLlama

2

È perfettamente sicuro e portatile. Si sta semplicemente utilizzando l'aritmetica del puntatore per indirizzare la memoria allocata dall'operatore new.

Il codice è equivalente a:

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

L'operatore indice x[idx] uguale (*(x +idx)) e sì idx potrebbe essere negativo. Tuttavia, è necessario assicurarsi che il puntatore dereferenziato stia puntando a un indirizzo di memoria valido.

Si noti che è possibile riscriverlo in molti modi (proprio come l'algebra).

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

Sì, è ben definito. Built-in operator[] è definito in termini di aritmetica del puntatore. Questo:

p[N] 

dove p è un puntatore e N è un numero intero, è equivalente alla seguente:

*(p + N) 

Un risultato interessante di questo è che questo:

N[p] 

è anche equivalente, perché l'addizione è commutativa.

+0

Quest'ultimo esempio presuppone l'utilizzo dell'operatore di pedice incorporato. – edmz

+3

@black: così fa il primo. E l'ho menzionato all'inizio del mio post. –

+0

Non sono sicuro se lo chiamerei "up" -shot. ;-p N [p] è mai stato utile? – Macke

3

È assolutamente soddisfacente, a condizione che non si tenti di trasferire un puntatore al di fuori dei limiti dell'array puntato da p.

Inoltre, è possibile impostare il puntatore q su qualsiasi elemento dell'array e, inoltre, su un elemento oltre l'array. (Non tentare di dereferenziare un elemento dopo la fine del sedile).

Alla fine della funzione, non dimenticare di delete[] p;.

7

Secondo Visual C++ (5.2.1 subscripting)

1 Un'espressione postfissa seguita da un'espressione tra parentesi quadre è un'espressione suffisso. Una delle espressioni deve avere il tipo "matrice di T" o "puntatore a T" e l'altra deve avere l'enumerazione senza ambito o il tipo integrato. Il risultato è di tipo "T." Il tipo "T" deve essere un tipo di oggetto completamente definito.65 L'espressione E1 [E2] è identica (per definizione) a * ((E1) + (E2)). ..

così si può usare qualunque tipo integrale che comprende tipo int e valori corrispondentemente negativi a condizione che il risultato dell'espressione *((E1)+(E2)) è ben formato.

Prendere in considerazione che per i tipi definiti dall'utente è possibile utilizzare un elenco di inizializzazione come indice. Per esempio

#include <iostream> 

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 (" << p.x << ", " << p.y << ")\n"; 
     return *this; 
    } 

    unsigned int r; 
};   

int main() 
{ 
    Circle circle(10); 

    circle[ { 0, 0 } ]; 
}  

L'output del programma è

Drawing a circle at (0, 0) 
Problemi correlati