2011-12-02 13 views
8

tengo el siguiente programa:Cuando resta direcciones de memoria, ¿por qué el resultado es más pequeño de lo que esperaba?

#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; 
} 

puedo visualizar el diseño X 's en la memoria, 10 cajas que contienen un int y una float, p1 señalarán al comienzo de la segunda caja (x[1]) y p2 señalador al comienzo de la sexta caja (x[5]):

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

es mi gráfico correcto? Si es así, ¿por qué es el resultado de i 4?
¿Tiene algunas dificultades para entender la resta de dos direcciones?

+0

Has etiquetado esta pregunta 'c', pero usas' cout' en tu ejemplo, que en su lugar es C++. ¿Qué estás usando? –

+0

Sí, supongo que es una pregunta en C, pero usé cout por simplicidad, supongo. – Kobe

+4

@DanielPryden No hará una diferencia en la respuesta, ¿o sí? – Szabolcs

Respuesta

23

Funciona así pointer arithmetic. Considere:

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

En este punto, p2 no tendrá el valor 101, sino más bien 100 + sizeof(x) (que digamos que es 8, por lo que 108). ¡Se ha incrementado no por uno, sino por un múltiplo de sizeof(x)! Por el contrario, restar un entero de un puntero en realidad resta múltiplos de sizeof(the pointed to type).

Así que ahora si haces int diff = p2 - p1, esperarías obtener 1, no 8! De lo contrario, restar el número que acaba de agregar no arrojaría el valor original. Por lo tanto, al restar un puntero a otro, no se obtiene la diferencia en las direcciones de memoria, sino la cantidad de elementos entre los dos punteros.

Por otra parte, los mandatos estándar que puntero de la resta no tiene sentido a menos que los dos punteros apuntan a elementos de la misma matriz (más correctamente, es un comportamiento indefinido y que también están autorizados a utilizar un puntero a "uno más allá del último elemento "incluso si no hay tal objeto allí).

Finalmente, ¿qué pasa si el compilador no conoce el tamaño del tipo de apuntado (es decir, los punteros son void*)? En este caso, la aritmética del puntero no está permitida en absoluto. Por ejemplo:

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

¹ Algunos compiladores pueden proporcionar la aritmética de punteros en void* como extension a la especificación del lenguaje. En este caso, esta declaración puede compilarse y el resultado dependería de la especificación de dicha extensión (por ejemplo, gcc terminaría con el valor 101).

+0

1 buena explicación – Kobe

+0

buena explicación, pero su ejemplo compilará, aunque con advertencias como- es. Lo que creo que está específicamente prohibido es agregar dos punteros, es decir, si trataste de agregar pyx después de eso. –

+0

@DanFego: Gracias por la captura de esto y que me permite aprender algo nuevo (yo no soy muy experimentado en C, como en "algunos de los puntos más finos de la que C y C++ difieren"). He actualizado la respuesta con una explicación de lo que sucede aquí. – Jon

Cuestiones relacionadas