2014-12-14 12 views
5

Secondo N1570 (C11 bozza) 6.5.6/8Operatori additivi:Qual è la logica per uno dopo l'ultimo elemento di un oggetto array?

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

sottoparagrafo 6.5.6/9 contiene inoltre:

Inoltre, se l'espressione P punti sia ad un elemento di un oggetto array o uno dopo l'ultimo elemento di un oggetto array, e l'espressione Q indica l'ultimo elemento dello stesso oggetto matrice, l'espressione ((Q)+1)-(P) delloha lo stesso valore di ((Q)-(P))+1 e come -((P)-((Q)+1)) e ha il valore zero se l'espressione P punta a uno dell'ultimo elemento di l'oggetto matrice, anche se l'espressione (Q)+1 non punta a un elemento dell'oggetto matrice. 106)

Questo giustifica aritmetica puntatore simili sia valida:

#include <stdio.h> 

int main(void) 
{ 
    int a[3] = {0, 1, 2}; 
    int *P, *Q; 

    P = a + 3; // one past the last element 
    Q = a + 2; // last element 

    printf("%td\n", ((Q)+1)-(P)); 
    printf("%td\n", ((Q)-(P))+1); 
    printf("%td\n", -((P)-((Q)+1))); 

    return 0; 
} 

ci si aspetterebbe di non consentire indicando elemento dell'array fuori dal campo, per cui dereferenziare agisce comportamento indefinito (superamento della matrice), quindi lo rende potenzialmente pericoloso. C'è qualche logica per questo?

+1

"Mi aspetterei di non consentire il puntamento all'elemento di matrice fuori limite, per il quale la dereferenza agisce come comportamento non definito". Il dereferenziamento di questo elemento non è consentito quindi non c'è alcun problema. "C'è qualche logica per questo?" Sì, troppi da elencare. Google per * intervalli semiaperti in C *. –

+0

C (normalmente) non esegue il controllo dei limiti, quindi il codice verrà compilato correttamente. Tuttavia, l'accesso alla memoria al di fuori dei limiti dell'elemento a cui si accede è un comportamento indefinito e può/porterà ad un evento di errore del segmento. – user3629249

risposta

7

Specificare l'intervallo su cui eseguire il loopover come half-closed interval[start, end), in particolare per gli indici di array, ha alcune proprietà gradevoli come Dijkstra osservato in one of his notes.

1) È possibile calcolare la dimensione dell'intervallo come una semplice funzione di end - start. In particolare, se l'intervallo è specificato in termini di indici di matrice, il numero di iterazioni eseguite dal ciclo sarà dato da end - start. Se l'intervallo era [start, end], il numero di iterazioni sarebbe stato end - start + 1 - molto fastidioso, non è vero? :)

2) La seconda osservazione di Dijsktra si applica solo al caso di indici integrali (non negativi) - specificando un intervallo come [start, end) e (start, end] entrambi hanno la proprietà menzionata in 1). Tuttavia, specificandolo come (start, end] è necessario consentire un indice di -1 per rappresentare un intervallo di loop compreso l'indice 0 - si sta consentendo un valore "innaturale" di -1 solo per il gusto di rappresentare l'intervallo. La convenzione [start, end) non presenta questo problema, perché end è un numero intero non negativo e quindi una scelta naturale quando si gestiscono indici di array.

L'obiezione di Dijsktra a consentire -1 ha le somiglianze di consentire un passato l'ultimo indirizzo valido del contenitore.Tuttavia, dal momento che la convenzione di cui sopra è stata utilizzata per così tanto tempo, è probabile che abbia convinto il comitato degli standard a fare questa eccezione.

3

La logica è piuttosto semplice. Il compilatore non è autorizzato a posizionare un array alla fine della memoria. Per illustrare, supponiamo di avere una macchina a 16 bit con puntatori a 16 bit. L'indirizzo basso è 0x0000. L'indirizzo alto è 0xffff. Se si dichiara char array[256] e il compilatore individua array all'indirizzo 0xff00, quindi tecnicamente la matrice si adatterà alla memoria, utilizzando gli indirizzi 0xff00 attraverso 0xffff incluso. Tuttavia, l'espressione

char *endptr = &array[256]; // endptr points one past the end of the array 

sarebbe equivalente a

char *endptr = NULL;   // &array[256] = 0xff00 + 0x0100 = 0x0000 

Il che significa che il ciclo seguente non avrebbe funzionato, dal momento che ptr sarà mai inferiore a 0

for (char *ptr = array; ptr < endptr; ptr++) 

Così sezioni hai citato semplicemente l'avvocato per "Non mettere gli array alla fine di una regione della memoria".


Nota storica: i processori x86 prime utilizzate uno schema memoria segmentata cui indirizzi di memoria dove specificato da un registro puntatore a 16 bit e un registro di segmento 16 bit. L'indirizzo finale è stato calcolato spostando il registro del segmento lasciato da 4 bit e aggiungendolo al puntatore, ad es.

pointer register 1234 
segment register AB00 
        ----- 
address in memory AC234 

Lo spazio di indirizzo risultante era 1MByte, ma c'erano end-of-memoria confini ogni 64Kbytes. Questa è una delle ragioni per cui si usa parlare l'avvocato invece di affermare, "Non mettere gli array alla fine della memoria" in inglese.

Problemi correlati