O padrão C99 diz em 6.5.16: 2:
Um operador de atribuição deve ter um valor modificável como seu operando esquerdo.
e em 6.3.2.1:1:
Um lvalue modificável é um lvalue que não possui um tipo de matriz, não possui um tipo incompleto, não possui um tipo qualificado de const e, se for uma estrutura ou união, não possui nenhum membro (incluindo, recursivamente, qualquer membro ou elemento de todos os agregados ou uniões contidos) com um tipo qualificado de const.
Agora, vamos considerar const
struct
um const
campo não- com .
typedef struct S_s {
const int _a;
} S_t;
Por padrão, o código a seguir é um comportamento indefinido (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
O problema semântico disso é que a entidade envolvente ( struct
) deve ser considerada gravável (não somente leitura), a julgar pelo tipo declarado da entidade ( S_t s1
), mas não deve ser considerada gravável pela redação do padrão (as 2 cláusulas no topo) por causa do const
campo _a
. O Padrão deixa claro para um programador que lê o código que a atribuição é realmente um UB, porque é impossível dizer que sem a definição de struct S_s ... S_t
tipo.
Além disso, o acesso somente leitura ao campo é imposto de forma sintática apenas de qualquer maneira. Não há como, de fato, alguns const
campos não- const
struct
serem realmente colocados no armazenamento somente leitura. Mas essa redação do padrão proíbe o código que deliberadamente rejeita o const
qualificador de campos nos procedimentos de acessador desses campos, assim ( é uma boa idéia qualificar os campos de estrutura em 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);
}
Portanto, por algum motivo, para que um todo struct
seja somente leitura, basta declará-loconst
const S_t s3;
Mas, para que um todo struct
seja não somente leitura, não é suficiente declará-lo sem const
.
O que eu acho que seria melhor, é:
- Restringir a criação de não-
const
estruturas comconst
campos e emitir um diagnóstico nesse caso. Isso deixaria claro que osstruct
campos contendo somente leitura são somente leitura. - Definir o comportamento em caso de gravação em um
const
campo pertencente a uma não-const
estrutura, a fim de tornar o código acima (*) compatível com a Norma.
Caso contrário, o comportamento não é consistente e difícil de entender.
Então, qual é a razão para o Padrão C considerar o const
-ness recursivamente, como ele diz?