que - Cosa real sobre "->" y "."
cdata xml version 1.0 encoding utf 8 (6)
Como p es una variable local (automática), se almacena en la pila. Por lo tanto, el compilador accede a él en términos de desplazamiento con respecto al puntero de pila (SP) o el puntero de marco (FP o BP, en arquitecturas donde existe). Por el contrario, * p se refiere a una dirección de memoria [generalmente] asignada en el montón, por lo que no se utilizan los registros de pila.
Siempre quise saber cuál es la diferencia real de cómo el compilador ve un puntero a una estructura (en C supongo) y una estructura en sí misma.
struct person p;
struct person *pp;
pp->age
, siempre me imagino que el compilador hace: "valor de pp + offset de atributo" age "en la estructura".
Pero, ¿qué hace con person.p
? Sería casi lo mismo. Para mí "el programador", p no es una dirección de memoria, es como "la estructura en sí", pero, por supuesto, no es así como el compilador la trata.
Mi conjetura es que es más una cosa sintáctica, y el compilador siempre hace (&p)->age
.
¿Estoy en lo correcto?
En ambos casos, la estructura y sus miembros son abordados por
dirección (persona) + desplazamiento (edad)
El uso de p con una estructura almacenada en la memoria de pila le da al compilador más opciones para optimizar el uso de la memoria. Podría almacenar solo la antigüedad, en lugar de la estructura completa si no se usa nada más, esto hace que el direccionamiento con la función anterior falle (creo que leer la dirección de una estructura detiene esta optimización).
Una estructura en la pila puede no tener ninguna dirección de memoria. Si la estructura es lo suficientemente pequeña y dura poco tiempo, se puede asignar a algunos de los registros de los procesadores (igual que para la optimización anterior para leer la dirección).
La respuesta corta: cuando el compilador no optimiza, tienes razón. Tan pronto como el compilador comienza a optimizar, solo se garantiza lo que el estándar c especifica .
Edición: Se eliminó la ubicación de pila / pila defectuosa para "pp->" ya que la estructura apuntada a estructura puede estar tanto en pila como en pila.
Esta es una pregunta que siempre me he preguntado.
vx
, el operador miembro , es válido solo para estructuras. v->x
, el miembro del operador de puntero , es válido solo para los punteros de estructura.
Entonces, ¿por qué tienen dos operadores diferentes, ya que solo se necesita uno? Por ejemplo, solo el .
operador podría ser utilizado; el compilador siempre sabe el tipo de v
, por lo que sabe qué hacer: vx
si v
es una estructura, (*v).x
si v
es un puntero de estructura.
Tengo tres teorías:
- miopía temporal por K&R (cuya teoría me gustaría ser falsa)
- facilitando el trabajo para el compilador (una teoría práctica, dado el tiempo de concepción de C :)
- legibilidad (cual teoría prefiero)
Desafortunadamente, no sé cuál (si alguna) es verdadera.
Las dos afirmaciones no son equivalentes, incluso desde la "perspectiva del compilador". La declaración p.age
traduce a la dirección de p
+ el desplazamiento de la age
, mientras que pp->age
traduce a la dirección contenida en pp
+ el desplazamiento de la age
.
La dirección de una variable y la dirección contenida en una variable (puntero) son cosas muy diferentes.
Digamos que la diferencia de edad es 5. Si p
es una estructura, su dirección podría ser 100, por lo que la p.age
referencia a la dirección 105.
Pero si pp
es un puntero a una estructura, su dirección podría ser 100, pero el valor almacenado en la dirección 100 no es el comienzo de una estructura de person
, es un puntero. Entonces, el valor en la dirección 100 (la dirección contenida en pp
) podría ser, por ejemplo, 250. En ese caso, pp->age
referencia a la dirección 255, no a 105.
p->q
es esencialmente azúcar sintáctica para (*p).q
que no hace referencia al puntero p
y luego va al campo apropiado q
dentro de él. Se guarda la escritura para un caso muy común (punteros a estructuras).
En esencia, ->
hace dos deferencias (diferencia de puntero, diferencia de campo) mientras .
solo hace uno (desreferencia de campo).
Debido al factor de desreferencia múltiple, el compilador no puede ser reemplazado completamente por una dirección estática y siempre incluirá al menos el cálculo de la dirección (los punteros pueden cambiar dinámicamente en tiempo de ejecución, por lo que las ubicaciones también cambiarán), mientras que en algunos casos,. Las operaciones pueden ser reemplazadas por el compilador con un acceso a una dirección fija (ya que la dirección de la estructura base también puede ser fija).
Actualizado (ver comentarios):
Tiene la idea correcta, pero hay una diferencia importante solo para las variables globales y estáticas : cuando el compilador ve p.age
para una variable global o estática, puede reemplazarla, en tiempo de compilación, con la dirección exacta del campo de age
dentro de la estructura.
En contraste, pp->age
debe compilar como "valor de pp + desplazamiento del campo age
", ya que el valor de pp puede cambiar en tiempo de ejecución.