2011-12-02 6 views
8

Ho il seguente programma:Quando sottrarre gli indirizzi di memoria, perché il risultato è più piccolo di quanto mi aspettassi?

#include <iostream> 

struct X 
{ 
    int a; 
    float b; 
} x[10], *p1, *p2; 

int main(int argc, char *argv[]) 
{ 
    p1 = &x[1]; 
    p2 = &x[5]; 

    int i = p2 - p1; 

    std::cout << i << std::endl; 
} 

I può visualizzare la layout X s' in memoria, 10 scatole contenenti un int e float, p1 indicheranno all'inizio della seconda casella (x[1]) e p2 puntamento all'inizio del 6 ° scatola (x[5]):

X 0 1 2 3 4 5 6 7 8 9 
     _______________________________ 
    b |__|__|__|__|__|__|__|__|__|__| 
    a |__|__|__|__|__|__|__|__|__|__| 
      |   |  
      |   | 
      p1   p2 

è la mia illustrazione corretta? In caso affermativo, perché il risultato è i 4?
Hai qualche difficoltà a capire la sottrazione di due indirizzi?

+0

Hai taggato questa domanda 'c', ma nel tuo esempio usi' cout', che invece è C++. Quale stai usando? –

+0

Sì, immagino sia una domanda su C, ma ho usato il cout per la semplicità, immagino. – Kobe

+4

@DanielPryden Non farà la differenza nella risposta, vero? – Szabolcs

risposta

23

Ecco come funziona pointer arithmetic. Considerate:

p1 = (x*)100; // invalid memory address, just an example! 
p2 = p1 + 1; 

A questo punto, p2 non avrà il valore 101, ma piuttosto 100 + sizeof(x) (che diciamo è 8, quindi 108). È stato incrementato non di uno, ma di un multiplo di sizeof(x)! Al contrario, la sottrazione di un intero da un puntatore sottrae effettivamente i multipli di sizeof(the pointed to type).

Quindi ora se si fa int diff = p2 - p1, ci si aspetterebbe di ottenere 1 indietro, non 8! Altrimenti, sottraendo il numero appena aggiunto non si otterrebbe il valore originale. Pertanto, sottraendo un puntatore da un altro non si ottiene la differenza negli indirizzi di memoria ma il numero di elementi tra i due puntatori.

Inoltre, i mandati standard che puntatore sottrazione non ha senso se i due puntatori puntano a elementi dello stesso array (più correttamente, è un comportamento indefinito e si è anche permesso di usare un puntatore a "uno dopo l'ultimo elemento "anche se non esiste un tale oggetto lì).

Infine, cosa succede se il compilatore non conosce la dimensione del tipo puntato (ad esempio i puntatori sono void*)? In questo caso, l'aritmetica del puntatore non è consentita affatto. Per esempio:

void* p = 100; 
void* x = p + 1; // does not compile¹ 

¹ Alcuni compilatori possono fornire l'aritmetica dei puntatori su void* come extension alla specifica del linguaggio. In questo caso questa affermazione può effettivamente essere compilata e il risultato dipenderebbe dalla specifica di detta estensione (ad esempio gcc finirebbe con il valore 101).

+0

+1 spiegazione Nice – Kobe

+0

Bella spiegazione, ma il tuo esempio verrà compilato, anche se con avvisi as- è. Ciò che ritengo sia specificamente proibito è l'aggiunta di due indicatori, ad esempio se si è tentato di aggiungere p e x successivamente. –

+0

@DanFego: Grazie per aver catturato questo e per avermi permesso di imparare qualcosa di nuovo (non ho molta esperienza in C, come in "alcuni dei punti migliori di dove C e C++ differiscono"). Ho aggiornato la risposta con una spiegazione di cosa succede qui. – Jon

Problemi correlati