C99標準は6.5.16:2で次のように述べています。
代入演算子は、左オペランドとして変更可能な左辺値を持つものとします。
6.3.2.1:1では:
変更可能な左辺値は、配列型がなく、不完全型がなく、const修飾型がなく、構造体または共用体である場合、メンバー(再帰的に、任意のメンバーを含む)がない左辺値ですまたは含まれるすべての集合体または共用体の要素)、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
non-の一部のフィールドをconst
struct
実際に読み取り専用ストレージに配置する方法はありません。しかし、このような標準の表現はconst
、これらのフィールドのアクセサープロシージャのフィールドの修飾子を故意にキャストするコードを非合法化します(Cの構造体のフィールドをconst修飾することは良い考えですか?):
(*)
#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
が読み取り専用ではない場合、それをw / oと宣言するだけでは不十分const
です。
私がより良いと思うのは、次のいずれかです。
- フィールドを
const
持つ非構造体の作成を制限し、そのconst
ような場合に診断を発行します。これにより、struct
含まれている読み取り専用フィールドがそれ自体が読み取り専用であることは明らかです。 - 上記のコード(*)を規格に準拠させるため
const
に、非const
構造体に属するフィールドに書き込む場合の動作を定義します。
そうしないと、動作に一貫性がなくなり、理解が難しくなります。
では、Cスタンダードがconst
-nessを再帰的に検討する理由は何ですか?