El estándar C99 dice en 6.5.16: 2:
Un operador de asignación tendrá un valor l modificable como su operando izquierdo.
y en 6.3.2.1:1:
Un lvalue modificable es un lvalue que no tiene un tipo de matriz, no tiene un tipo incompleto, no tiene un tipo const y, si es una estructura o unión, no tiene ningún miembro (incluido, recursivamente, ningún miembro o elemento de todos los agregados o uniones contenidos) con un tipo calificado const.
Ahora, consideremos un no const structcon un constcampo.
typedef struct S_s {
const int _a;
} S_t;
Por norma, el siguiente código es comportamiento indefinido (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
El problema semántico con esto es que la entidad que la encierra ( struct) debe considerarse escribible (no solo de lectura), a juzgar por el tipo declarado de la entidad ( S_t s1), pero no debe considerarse escribible por la redacción del estándar (las 2 cláusulas en la parte superior) debido al constcampo _a. El estándar no deja claro para un programador que lee el código que la asignación es en realidad una UB, porque es imposible decir que sin la definición de struct S_s ... S_ttipo.
Además, el acceso de solo lectura al campo solo se aplica sintácticamente de todos modos. No hay forma de que algunos constcampos que no const structsean realmente se coloquen en almacenamiento de solo lectura. Pero tal redacción de los proscritos estándar del código que desecha deliberadamente el constcalificador de campos en los procedimientos de acceso de estos campos, así ( ¿Es una buena idea consolidar los campos de estructura en C? ):
(*)
#include <stdlib.h>
#include <stdio.h>
typedef struct S_s {
const int _a;
} S_t;
S_t *
create_S(void) {
return calloc(sizeof(S_t), 1);
}
void
destroy_S(S_t *s) {
free(s);
}
const int
get_S_a(const S_t *s) {
return s->_a;
}
void
set_S_a(S_t *s, const int a) {
int *a_p = (int *)&s->_a;
*a_p = a;
}
int
main(void) {
S_t s1;
// s1._a = 5; // Error
set_S_a(&s1, 5); // OK
S_t *s2 = create_S();
// s2->_a = 8; // Error
set_S_a(s2, 8); // OK
printf("s1.a == %d\n", get_S_a(&s1));
printf("s2->a == %d\n", get_S_a(s2));
destroy_S(s2);
}
Entonces, por alguna razón, para que un todo structsea de solo lectura es suficiente declararloconst
const S_t s3;
Pero para que un todo structno sea de solo lectura, no es suficiente declararlo sin él const.
Lo que creo que sería mejor es:
- Para restringir la creación de no
constestructuras conconstcampos, y emitir un diagnóstico en tal caso. Eso dejaría en claro que losstructcampos que contienen solo lectura son solo de lectura. - Para definir el comportamiento en caso de escribir en un
constcampo perteneciente a unaconstestructura no estructurada para que el código anterior (*) cumpla con el Estándar.
De lo contrario, el comportamiento no es consistente y difícil de entender.
Entonces, ¿cuál es la razón por la cual C Standard considera la constrecursividad recursivamente, como dice?

Respuestas:
Desde una perspectiva de tipo solo, no hacerlo sería poco sólido (en otras palabras: terriblemente roto e intencionalmente poco confiable).
Y eso se debe a lo que "=" significa en una estructura: es una asignación recursiva. De esto se deduce que eventualmente
s1._a = <value>ocurre "dentro de las reglas de escritura". Si el estándar permite esto para losconstcampos "anidados" , está agregando una seria inconsistencia en su definición del sistema de tipos como una contradicción explícita (también podríaconstdescartar la característica, ya que se volvió inútil y poco confiable por su propia definición).Su solución (1), por lo que yo entiendo, está forzando innecesariamente a que toda la estructura sea
constcada vez que uno de sus campos esconst. De esta manera,s1._b = bsería ilegal para un._bcampo no constante en un campo no constante ques1contiene unconst a.fuente
CApenas tiene un sistema de tipo de sonido (más como un montón de cajas de esquina atadas entre sí a lo largo de los años). Además, la otra forma de asignar la tarea a astructesmemcpy(s_dest, s_src, sizeof(S_t)). Y estoy bastante seguro de que es la forma en que se implementa. Y en tal caso, incluso el "sistema de tipos" existente no le prohíbe hacerlo.La razón es que los campos de solo lectura son de solo lectura. No hay gran sorpresa allí.
Asume erróneamente que el único efecto está en la colocación en ROM, lo que de hecho es imposible cuando hay campos adyacentes no constantes. En realidad, los optimizadores pueden asumir que las
constexpresiones no están escritas y optimizar en función de eso. Por supuesto, esa suposición no se cumple cuando existen alias no constantes.Su solución (1) rompe el código legal y razonable existente. Eso no va a suceder. Su solución (2) prácticamente elimina el significado de
constlos miembros. Si bien esto no romperá el código existente, parece carecer de una justificación.fuente
constcampos no están escritos, porque siempre puedes usarmemsetomemcpy, y eso incluso sería compatible con el Estándar. (1) puede implementarse como, como mínimo, advertencia adicional, habilitada por una bandera. La justificación de (2) es que, bueno, exactamente, no hay forma de que un componentestructpueda considerarse no escribible cuando toda la estructura es escribible.operator=en términos de los miembros, y por lo tanto no defineoperator=cuándo es un miembroconst. C y C ++ siguen siendo compatibles aquí.memcpy? En cuanto a otras razones, está bien, es legado, pero ¿por qué se hizo de esa manera en primer lugar?memcpyes correcto. AFACITO La cita de John Bode en su otra pregunta es correcta: su código escribe en un objeto calificado const y, por lo tanto, NO es una queja estándar, fin de la discusión.