Considere la siguiente estructura:
struct s {
int a, b;
};
Típicamente 1 , esta estructura tendrá tamaño 8 y alineación 4.
¿Qué pasa si creamos dos struct sobjetos (más precisamente, escribimos en el almacenamiento asignado dos de estos objetos), con el segundo objeto superpuesto al primero?
char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4
// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};
printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);
¿Hay algo sobre este programa de comportamiento indefinido? Si es así, ¿dónde queda indefinido? Si no es UB, ¿se garantiza que siempre imprima lo siguiente?
o2.a=3
o2.b=4
o1.a=1
o1.b=3
En particular, quiero saber qué sucede con el objeto señalado por o1cuándo o2, que se superpone, está escrito. ¿Todavía está permitido acceder a la parte no ocultada ( o1->a)? ¿Acceder a la parte con capa es o1->bsimplemente lo mismo que acceder o2->a?
¿Cómo se aplica el tipo efectivo aquí? Las reglas son lo suficientemente claras cuando habla de objetos no superpuestos y punteros que apuntan a la misma ubicación que la última tienda, pero cuando comienza a hablar sobre el tipo efectivo de porciones de objetos u objetos superpuestos, es menos claro.
¿Cambiaría algo si la segunda escritura fuera de un tipo diferente? Si los miembros fueran decir inty shortno dos ints?
Aquí hay un godbolt si quieres jugar con él allí.
1 Esta respuesta se aplica a plataformas donde este no es el caso también: por ejemplo, algunas pueden tener tamaño 4 y alineación 2. En una plataforma donde el tamaño y la alineación son las mismas, esta pregunta no se aplicaría ya que los objetos alineados y superpuestos ser imposible, pero no estoy seguro de si hay alguna plataforma como esa.

Respuestas:
Básicamente, esta es toda el área gris en el estándar; La estricta regla de alias especifica casos básicos y deja que el lector (y los proveedores del compilador) completen los detalles.
Se han realizado esfuerzos para escribir una regla mejor, pero hasta ahora no han dado lugar a ningún texto normativo y no estoy seguro de cuál es el estado de esto para C2x.
Como mencioné en mi respuesta a su pregunta anterior, la interpretación más común es que
p->qsignifica(*p).qy el tipo efectivo se aplica a todos*p, a pesar de que luego procedemos a aplicar.q.Según esta interpretación,
printf("o1.a=%d\n", o1->a);causaría un comportamiento indefinido ya que el tipo efectivo de la ubicación*o1no lo ess(ya que parte de ella se ha sobrescrito).La justificación de esta interpretación se puede ver en una función como:
Con esta interpretación, la última línea podría optimizarse
puts("5");, pero sin ella, el compilador tendría que considerar que la llamada a la función puede haber sidof(o1, o2);y, por lo tanto, perder todos los beneficios que supuestamente proporciona la estricta regla de alias.Un argumento similar se aplica a dos tipos de estructura no relacionados que tienen un
intmiembro con un desplazamiento diferente.fuente
f(s* s1, s* s2), sinrestrict, el compilador no puede asumirs1ys2son punteros diferentes. Creo que , una vez másrestrict, ni siquiera puede suponer que no se superponen parcialmente. IAC, no veo que laf()analogía demuestre bien la preocupación de OP . Buena suerte sin gruñir. UV para la primera mitad.s1 == s2se permitiría, pero no una superposición parcial. (La optimización en mi ejemplo de código todavía podría realizarse sis1 == s2)intlugar de structs (y un sistema con_Alignof(int) < sizeof(int)).p->qy(*p).q. Esto puede ser cierto para la interpretación de tipo como usted dice, pero no es cierto desde un punto de vista operativo. Es importante para los accesos concurrentes a la misma estructura que el acceso de un miembro no implique el acceso de ningún otro miembro.E1.E2expresión no realiza el acceso (me refiero a laE1expresión completa . Algunas de sus subexpresiones pueden realizar el acceso. Es decir, siE1es así(*p), leer el valor del puntero al evaluar elpacceso, pero la evaluación de*po(*p)no realiza ninguna acceso). La regla de alias estricta no se aplica en caso de que no haya acceso.