2016-03-12 14 views
11

Sono il seguente programma:Array dichiarato come int v [100], ma e (v [100]) non avverte

#include <stdio.h> 

int main() { 

    int v[100]; 
    int *p; 

    for (p = &(v[0]); p != &(v[100]); ++p) 
     if ((*p = getchar()) == EOF) { 
      --p; 
      break; 
     } 

    while (p != v) 
     putchar(*--p); 

    return 0; 
} 

E questa è l'uscita del gcc --version sul terminale:

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 
Apple LLVM version 7.0.2 (clang-700.1.81) 
Target: x86_64-apple-darwin15.3.0 
Thread model: posix 

Perché ottenere l'indirizzo dell'elemento dopo l'ultima di una serie mi dà nessun avvertimento ma ottenere ad esempio l'indirizzo del v[101] mi dà il seguente avviso

test.c:8:29: warning: array index 101 is past the end of the array (which 
     contains 100 elements) [-Warray-bounds] 
    for(p = &(v[0]); p != &(v[101]); ++p) 
          ^~~~ 
test.c:5:5: note: array 'v' declared here 
    int v[100]; 
    ^
1 warning generated. 

So che gli elementi di indicizzazione fuori dai limiti di un buffer sono comportamento non definito, quindi perché il compilatore non si lamenta del primo caso?

+1

Il programma invocherà * comportamento non definito * se vengono letti solo zero caratteri prima di EOF. – MikeCAT

+2

Non dovresti decrementare 'p' su' EOF'. – chqrlie

+0

@chqrlie Dici questo perché nel seguente ciclo while prima decremento 'p' e poi dereferenziarlo? – nbro

risposta

14

Lo spostamento del puntatore a un passato l'ultimo elemento dell'array è consentito a meno che non si denomini il puntatore, pertanto il programma è valido se uno o più caratteri vengono letti prima di colpire EOF.

N1256 6.5.2.1 Array indicizzazione di

La definizione di operatore pedice [] è che E1 [E2] è identica a (* ((E1) + (E2))).

N1256 6.5.3.2 Coordinate e indirezione operatori

Se l'operando è il risultato di un unario * operatore, né tale operatore né l'operatore & viene valutata e il risultato è come se entrambi erano omesso, tranne per il fatto che i vincoli sugli operatori si applicano ancora e il risultato non è un lvalue . Allo stesso modo, se l'operando è il risultato di un operatore [], né l'operatore & né l'unario * implicato da [] viene valutato e il risultato è come se l'operatore sia stato rimosso e l'operatore [] sia stato rimosso cambiato in un operatore +.

N1256 6.5.6 Operatori additivi

Inoltre, se i punti espressione P all'ultimo elemento di un oggetto array, l'espressione (P) +1 punti uno dopo l'ultimo elemento della oggetto matrice, e se i punti espressione Q uno dopo l'ultimo elemento di un oggetto array, l'espressione (Q) -1 indica l'ultimo elemento dell'oggetto matrice

+2

Onestamente, le citazioni non rispondono alla mia domanda. Stai anche dicendo che lo spostamento di un puntatore a uno dopo l'ultimo elemento di un array è consentito (e le citazioni non sono in realtà correlate a ciò che stai dicendo), ma sto anche _also_ in realtà ottenendo l'indirizzo dell'elemento oltre il ultimo elemento (accedendo per primo) ... – nbro

+0

@nbro C'è anche una citazione che '& * X' è definita come' X', quindi quando si combina questo con la prima citazione in questa risposta, si vede che '& v [100]' è definito per significare '(v + 100)' che è legale secondo l'ultima citazione –

+0

Per dirla in un altro modo, parlando di '* (v + 100)' è solo indefinito se si tenta di accedere alla posizione di memoria designata (che si farebbe in qualsiasi contesto diverso dal mettere '&' o' sizeof' prima di esso) –

0

tratta di compa compatibilità con codice scritto in modo scorretto.

Come MikeCAT ha citato, per un array int ar[N], l'espressione ar+N è valida e restituisce un puntatore che punta alla posizione passata-fine. Sebbene questo puntatore non possa essere dereferenziato, può essere paragonato a qualsiasi altro puntatore nell'array, che consente di scrivere il bel ciclo for (p = ar; p != ar+N; ++p).

Inoltre, i programmatori piace scrivere codice leggibile, e probabilmente, se si vuole un puntatore all'elemento esimo i di un array, la scrittura &ar[i] trasmette il vostro intento più chiaro che scrivere ar + i.

combinare questi due, e si otterrà i programmatori che scrivono &ar[N] per ottenere il puntatore past-the-end, e mentre questo è tecnicamente accede a un indice di matrice non valida, nessun compilatore potrà mai implementare questa come qualsiasi altra cosa che ar + N - in Infatti, il compilatore dovrebbe fare di tutto per farlo in modo diverso. Abbastanza lontano in effetti.

Quindi, dal momento che qualsiasi compilatore che non ragiona in modo rigoroso sul comportamento indefinito farà la cosa che i programmatori si aspettano per l'espressione, non c'è motivo per non scriverlo, e così molte persone lo hanno scritto. E ora abbiamo enormi basi di codice che usano questo idioma, il che significa che anche i compilatori moderni con il loro valore di tracciamento e ragionamento su comportamento indefinito devono supportare questo idioma per la compatibilità. E dal momento che gli avvertimenti di Clang sono pensati per essere utili, questo avvertimento particolare è stato scritto in modo da non avvertire di un caso che funzionerà comunque, per un senso di pedanteria fuori luogo.

+0

* Si tratta di compatibilità con il codice scritto in modo scadente. * Non sono d'accordo. Non c'è nulla di sbagliato nel costruire un puntatore uno oltre la fine di un array. Il comportamento è ben definito ed è una tecnica utile. –