Стандарт C99 говорить в 6.5.16: 2:
Оператор призначення повинен мати змінне значення як його лівий операнд.
та в 6.3.2.1:
Зміна lvalue - це значення, яке не має типу масиву, не має неповного типу, не має типу const, і якщо це структура чи об'єднання, не має жодного члена (включаючи, рекурсивно, будь-який член або елемент усіх агрегатів або об'єднань, що містяться) з типом, що відповідає кваліфікації.
Тепер давайте розглянемо не const structз constполем.
typedef struct S_s {
const int _a;
} S_t;
За стандартом наступний код - це невизначена поведінка (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
Семантична проблема з цим полягає в тому, що об'єкт, що додає ( struct), повинен вважатися записуваним (не тільки для читання), судячи з заявленого типу сутності ( S_t s1), але не повинен вважатися записуваним формулюванням стандарту (2 пункти вгорі) через constполе _a. Стандарт не дає зрозуміти програмісту, який читає код, що призначення є насправді UB, оскільки неможливо сказати, що без визначення struct S_s ... S_tтипу.
Більше того, доступ до поля лише для читання в будь-якому разі виконується лише синтаксично. Неможливо, що деякі constполя, які не const structє, дійсно розміщуватимуться для зберігання лише для читання. Але таке формулювання стандарту перекриває код, який свідомо відганяє constкласифікатор полів у процедурах аксесуарів цих полів, як-от так ( чи гарна ідея const-кваліфікувати поля структури в 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);
}
Тож чомусь для цілого, structякий потрібно прочитати, достатньо лише оголосити цеconst
const S_t s3;
Але для того, щоб ціле structне було для читання, його недостатньо лише оголосити без огляду const.
Я вважаю, що було б краще:
- Обмежувати створення
constнеструктур зconstполями та видавати діагностику в такому випадку. Це дозволило б зрозуміти, що поля, якіstructмістять лише читання, є лише для читання. - Визначити поведінку у випадку запису в
constполе, що належитьconstнеструктурі, щоб зробити код вище (*) відповідним Стандарту.
Інакше поведінка не є послідовною і важко зрозуміти.
Отже, що є причиною того, що C Standard вважає const-ness рекурсивно, як це висловлюється?