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
struct
con un const
campo.
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 const
campo _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_t
tipo.
Además, el acceso de solo lectura al campo solo se aplica sintácticamente de todos modos. No hay forma de que algunos const
campos que no const
struct
sean realmente se coloquen en almacenamiento de solo lectura. Pero tal redacción de los proscritos estándar del código que desecha deliberadamente el const
calificador 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 struct
sea de solo lectura es suficiente declararloconst
const S_t s3;
Pero para que un todo struct
no 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
const
estructuras conconst
campos, y emitir un diagnóstico en tal caso. Eso dejaría en claro que losstruct
campos que contienen solo lectura son solo de lectura. - Para definir el comportamiento en caso de escribir en un
const
campo perteneciente a unaconst
estructura 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 const
recursividad recursivamente, como dice?