[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