Norma C99 mówi w 6.5.16: 2:
Operator przypisania ma modyfikowalną wartość jako lewy operand.
oraz w 6.3.2.1:1:
Zmienna wartość jest wartością, która nie ma typu tablicowego, nie ma typu niekompletnego, nie ma typu stałego kwalifikowania, a jeśli jest strukturą lub związkiem, nie ma żadnego elementu (w tym, rekurencyjnie, żadnego elementu lub element wszystkich zawartych agregatów lub związków) o typie const.
Teraz rozważmy nie- const structz constpola.
typedef struct S_s {
const int _a;
} S_t;
Standardowo następujący kod jest niezdefiniowanym zachowaniem (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
Problem semantyczny polega na tym, że encja zamykająca ( struct) powinna być uważana za zapisywalną (nie tylko do odczytu), sądząc po zadeklarowanym typie encji ( S_t s1), ale nie powinna być uważana za zapisywalną na podstawie sformułowania standardowego (2 klauzule na górze) z powodu constpola _a. Standard sprawia, że dla programisty czytającego kod nie jest jasne, że przypisanie jest w rzeczywistości UB, ponieważ nie można powiedzieć, że bez definicji struct S_s ... S_ttypu.
Co więcej, dostęp tylko do odczytu do pola jest i tak egzekwowany tylko syntaktycznie. Nie ma możliwości, aby niektóre constpola nie- const structtak naprawdę zostały umieszczone w pamięci tylko do odczytu. Ale takie sformułowanie standardu zakazuje kodu, który celowo odrzuca constkwalifikator pól w procedurach akcesyjnych tych pól, podobnie jak ( Czy dobrym pomysłem jest stałe kwalifikowanie pól struktury w 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);
}
Z jakiegoś powodu, aby całość structbyła tylko do odczytu, wystarczy ją zadeklarowaćconst
const S_t s3;
Ale aby całość structnie była tylko do odczytu, nie wystarczy zadeklarować jej brak const.
To, co moim zdaniem byłoby lepsze, to:
- Ograniczanie tworzenia niestruktur za
constpomocąconstpól i w takim przypadku wydawanie diagnozy. To by wyjaśniało, żestructzawierające pola tylko do odczytu jest tylko do odczytu. - Zdefiniowanie zachowania w przypadku zapisu w
constpolu należącym doconststruktury innej niż strukturalna w celu dostosowania powyższego kodu (*) do standardu.
W przeciwnym razie zachowanie nie będzie spójne i trudne do zrozumienia.
Jaki jest zatem powód, dla którego C Standard rozważa constrekurencyjność, jak to ujmuje?