Lo standard C99 dice in 6.5.16: 2:
Un operatore di assegnazione deve avere un valore modificabile come operando di sinistra.
e in 6.3.2.1:1:
Un lvalue modificabile è un lvalue che non ha un tipo di array, non ha un tipo incompleto, non ha un tipo con qualifica costante e se è una struttura o unione, non ha alcun membro (incluso, ricorsivamente, qualsiasi membro o elemento di tutti gli aggregati o sindacati contenuti) con un tipo con qualifica const.
Consideriamo ora un non const structcon un constcampo.
typedef struct S_s {
const int _a;
} S_t;
Per impostazione predefinita, il codice seguente è un comportamento indefinito (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
Il problema semantico con questo è che l'entità che racchiude ( struct) dovrebbe essere considerata scrivibile (non di sola lettura), a giudicare dal tipo dichiarato dell'entità ( S_t s1), ma non dovrebbe essere considerata scrivibile dalla formulazione di standard (le 2 clausole in alto) a causa del constcampo _a. Lo standard rende poco chiaro per un programmatore che legge il codice che l'assegnazione è in realtà un UB, perché è impossibile dirlo senza la definizione di struct S_s ... S_ttipo.
Inoltre, l'accesso in sola lettura al campo viene comunque applicato solo sintatticamente. Non è possibile che alcuni constcampi di non- const structdavvero vengano collocati in un archivio di sola lettura. Ma una tale formulazione di standard mette al bando il codice che elimina deliberatamente il constqualificatore di campi nelle procedure accessorie di questi campi, in questo modo ( è una buona idea const-qualificare i campi della struttura in 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);
}
Quindi, per qualche motivo, per un intero structessere di sola lettura è sufficiente dichiararloconst
const S_t s3;
Ma per un intero structnon essere di sola lettura non è sufficiente dichiararlo senza const.
Quello che penso sarebbe meglio, è:
- Vincolare la creazione di non
conststrutture conconstcampi ed emettere una diagnostica in tal caso. Ciò chiarirebbe che istructcampi di sola lettura contenenti sono di sola lettura. - Definire il comportamento in caso di scrittura su un
constcampo appartenente a una nonconststruttura in modo da rendere il codice sopra (*) conforme allo Standard.
Altrimenti il comportamento non è coerente e difficile da capire.
Quindi, qual è la ragione per cui C Standard considera constricorsivamente -ness, come dice?