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?