[C con Clase] Punteros a miembros de estructuras

Steven Davidson srd4121 en njit.edu
Vie Abr 9 18:55:50 CEST 2010


Hola Vicent,

Vicent wrote:
> He estado leyendo el interesante capítulo sobre cómo declarar punteros
> a elementos de una estructura:
> 
> http://c.conclase.net/curso/index.php?cap=041#inicio
> 
> 
> Yo creía que esto era o podría ser correcto:
> 
> 
> // defino una estructura :
> 
> typedef struct Cosa {
>     double a ;
>     double b ;
>     double c ;
> } Cosa ;
> 
> ...
> 
> // dentro del main :
> 
> Cosa una_cosa ;   // Un objeto de tipo "Cosa"
> double *p ;      // Un puntero a un dato de tipo "double"
> 
> una_cosa.a = 1.1 ;  una_cosa.b = 2.2 ;  una_cosa.c = 3.3 ;
> 
> p = &(cosa.a) ;   // ESTO YO CREÍA QUE VALÍA. ¿NO ES ASÍ?
> 
> 
> 
> Estoy interesado en saber si un código así funcionaría, sería
> correcto, etc.
> 

Sí. El código que has escrito es correcto. Esto es así porque estás 
accediendo a la memoria de una variable existente de una estructura 
también existente. Es decir, 'cosa' es una variable de tipo 'Cosa' y por 
tanto sus miembros son variables que también existen.

El capítulo 41 del curso habla de punteros a miembros sin que aún 
existan en memoria tales miembros. Usando tu ejemplo, esto sería:

double Cosa::*precio = &Cosa::b;

Como puedes ver, no estamos usando variables. 'Cosa' es el tipo de la 
estructura. Técnicamente, estos "punteros" realmente son desplazamientos 
a partir del comienzo de la estructura. Al combinar este tipo de puntero 
a miembro con una variable existente, podemos acceder al miembro sin 
usar su nombre definido. Por ejemplo,

Cosa una_cosa;
BYTE *ptr = reinterpret_cast< Cosa * >( una_cosa );
double *precio = reinterpret_cast< double * >( ptr+sizeof(double) );

El miembro 'a' es el primer miembro en la estructura y por tanto siempre 
estará al principio, por lo que estará a 0 bytes de desplazamiento a 
partir de la variable de la estructura. En nuestro ejemplo,

&una_cosa == &una_cosa.a

Digamos que 'una_cosa' está en la dirección de memoria: 0xAA33FF00.

El miembro 'b' es el segundo miembro en la estructura y por tanto 
siempre estará a 'sizeof(double)' de la estructura, porque 'a' ocupa 
'sizeof(double)' bytes. Es decir,

&una_cosa + sizeof(double) == &una_cosa.b

Mirando las direcciones de memoria, tenemos que,

&una_cosa.b = 0xAA33FF00 + 8 = 0xAA33FF08

(Nota: suponemos que 'double' ocupa 8 bytes).

Por último, el miembro 'c' es el tercer miembro de la estructura. 
Acabamos con la siguiente dirección de memoria:

&una_cosa.c = 0xAA33FF00 + 16 = 0xAA33FF10

Por lo tanto, podemos referir a cada miembro al conocer su dirección de 
memoria relativa a la de la variable de la estructura. De hecho, existen 
macros en ANSI C para este tipo de cosas llamada 'offsetof()' en 
<stddef.h> o en C++ se llama <cstddef>. Puedes echar un vistazo yendo a: 
http://c.conclase.net/librerias/?ansilib=stddef#inicio

En C++, este comportamiento forma parte del lenguaje y por tanto, no 
usaríamos 'offsetof()'. Sin embargo, el mecanismo es técnicamente el 
mismo, pero obviamente C++ controla la concordancia de tipos, porque 
como todos sabemos, C++ es un lenguaje fuertemente tipificado.

Al hacer,

double Cosa::*precio = &Cosa::b;

Técnicamente, 'precio' no es un puntero de verdad sino más bien un 
desplazamiento. Obviamente, necesitamos una variable para combinarla con 
'precio' porque la variable tiene una dirección de memoria "verdadera". 
Por ejemplo,

una_cosa.*precio = 10.43;

Esto es una forma indirecta de referirnos a 'una_cosa.b'. En este 
sentido, se parece a un puntero, por la indirección.


Espero haber aclarado el tema.

Steven





Más información sobre la lista de distribución Cconclase