Tiêu chuẩn C99 cho biết trong 6.5.16: 2:
Một toán tử gán sẽ có một giá trị có thể sửa đổi là toán hạng bên trái của nó.
và trong 6.3.2.1:1:
Một giá trị có thể sửa đổi là một giá trị không có kiểu mảng, không có kiểu không hoàn chỉnh, không có kiểu đủ điều kiện và nếu nó là cấu trúc hoặc liên kết, không có bất kỳ thành viên nào (bao gồm, đệ quy, bất kỳ thành viên nào hoặc phần tử của tất cả các tập hợp hoặc kết hợp có chứa với một loại đủ điều kiện const.
Bây giờ, hãy xem xét một không phải const
struct
với một const
lĩnh vực.
typedef struct S_s {
const int _a;
} S_t;
Theo tiêu chuẩn, mã sau đây là hành vi không xác định (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
Vấn đề ngữ nghĩa với điều này là thực thể kèm theo ( struct
) phải được coi là có thể ghi (không chỉ đọc), được đánh giá theo loại thực thể được khai báo ( S_t s1
), nhưng không nên được xem là có thể ghi theo cách diễn đạt của tiêu chuẩn (2 mệnh đề trên đầu trang) vì const
lĩnh vực _a
. Tiêu chuẩn làm cho không rõ ràng đối với một lập trình viên đọc mã rằng bài tập thực sự là một UB, bởi vì không thể nói rằng đó là định nghĩa về struct S_s ... S_t
kiểu.
Hơn nữa, quyền truy cập chỉ đọc vào trường chỉ được thực thi theo cú pháp. Không có cách nào một số const
lĩnh vực không const
struct
thực sự sẽ được đặt vào bộ lưu trữ chỉ đọc. Nhưng cách diễn đạt tiêu chuẩn đó vượt quá mã mà cố tình bỏ đi const
vòng loại của các trường trong thủ tục truy cập của các trường này, như vậy ( Có phải là một ý tưởng tốt để cấu thành các trường cấu trúc trong 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);
}
Vì vậy, vì một số lý do, toàn bộ struct
chỉ đọc là đủ để khai báo nóconst
const S_t s3;
Nhưng đối với toàn bộ struct
là không chỉ đọc thì không đủ để khai báo nó const
.
Những gì tôi nghĩ sẽ tốt hơn, là:
- Để hạn chế việc tạo ra các
const
cấu trúc phiconst
trường với các trường và đưa ra chẩn đoán trong trường hợp đó. Điều đó sẽ làm rõ rằng cácstruct
trường chỉ đọc có chứa chính nó chỉ đọc. - Để xác định hành vi trong trường hợp ghi vào
const
trường không thuộcconst
cấu trúc để làm cho mã ở trên (*) tuân thủ Tiêu chuẩn.
Nếu không thì hành vi không nhất quán và khó hiểu.
Vậy, lý do nào để C Standard cân nhắc - tính const
đệ quy, như nó đặt ra?