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
struct
con un const
campo.
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 const
campo _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_t
tipo.
Inoltre, l'accesso in sola lettura al campo viene comunque applicato solo sintatticamente. Non è possibile che alcuni const
campi di non- const
struct
davvero vengano collocati in un archivio di sola lettura. Ma una tale formulazione di standard mette al bando il codice che elimina deliberatamente il const
qualificatore 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 struct
essere di sola lettura è sufficiente dichiararloconst
const S_t s3;
Ma per un intero struct
non essere di sola lettura non è sufficiente dichiararlo senza const
.
Quello che penso sarebbe meglio, è:
- Vincolare la creazione di non
const
strutture conconst
campi ed emettere una diagnostica in tal caso. Ciò chiarirebbe che istruct
campi di sola lettura contenenti sono di sola lettura. - Definire il comportamento in caso di scrittura su un
const
campo appartenente a una nonconst
struttura 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 const
ricorsivamente -ness, come dice?