Giả sử chúng ta có đoạn mã sau:Hạn chế truy cập lĩnh vực giữa hai đối tượng cùng loại trong gcc
typedef struct {
int f1;
int f2;
} t_str;
int f(t_str* p, t_str* q)
{
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
p[0].f1++;
q[0].f2++;
return 0;
}
Khi chúng tôi biên dịch nó (tôi đã sử dụng gcc-5.1.0) với O3 tùy chọn, trình biên dịch được người lắp ráp sau đây:
f:
.LFB0:
.cfi_startproc
movl 8(%esp), %edx
movl 4(%esp), %ecx
movl 4(%edx), %eax
addl $9, (%ecx)
addl $9, %eax
movl %eax, 4(%edx)
xorl %eax, %eax
ret
.cfi_endproc
Điều đó có nghĩa là gcc đã quyết định truy cập vào trường f1 của p và truy cập vào trường f2 của q không bao giờ là bí danh. Tôi đoán điều này xuất phát từ giả định rằng hai đối tượng cùng loại không bao giờ chồng lên nhau hoặc chúng giống nhau. Nhưng tôi không tìm thấy vấn đề trong tiêu chuẩn.
Vì vậy, vui lòng, bất kỳ ai có thể tìm thấy vấn đề này theo tiêu chuẩn hoặc một điểm khác tại sao gcc giới hạn quyền truy cập trường hoặc nhận xét điều gì đã xảy ra?
UPD:
Vâng, tôi nghĩ về khoản 7 của Phần 6.5 quá, nhưng nó sẽ được thoải mái hơn cho tôi để có một cái gì đó như thế ở dạng rõ ràng cho tất cả các đối tượng:
6,5. 16,1 Simple phân
3 Nếu giá trị được lưu trữ trong một đối tượng được đọc từ một đối tượng chồng lấn trong bất kỳ cách nào lưu trữ các đối tượng đầu tiên, sau đó các chồng chéo sẽ là chính xác và hai đối tượng có trách nhiệm đủ điều kiện hoặc phiên bản không đủ tiêu chuẩn của loại tương thích; nếu không, hành vi là không xác định.
Rất tiếc, không thể sử dụng quy tắc này tại đây.
Bây giờ nhìn, nếu vì mã trên tôi thực hiện các chức năng sau:
void main()
{
char * c = malloc(12);
memset(c, 0, 12);
f((t_str *)(c + 4), (t_str *)c);
printf("%d %d %d\n", ((t_str *)c)->f1, ((t_str *)c)->f2, ((t_str *)(c + 4))->f2);
}
Bây giờ tôi nhận được sau khi thực hiện:
$ gcc-5.1.0 test1.c -O3 && ./a.out
0 9 0
$ gcc-5.1.0 test1.c -O0 && ./a.out
0 18 0
Vậy làm thế nào bạn nghĩ là mã này có hiệu lực? Bởi vì tôi không shure nếu nó statisfy Khoản 7 của Mục 6.5.
PS: điều thú vị:
$ gcc-5.1.0 test1.c -O3 -fwhole-program && ./a.out
0 10 0
$ gcc-5.1.0 test1.c -O3 -flto && ./a.out
0 10 0
Tôi không chắc tôi hiểu câu hỏi của bạn. Bạn có ý nghĩa gì với "bí danh" và "trùng lặp"? Trình biên dịch có vẻ đúng như thế nào, miễn là p và q không thay đổi giữa các lần truy cập (ví dụ: thông qua việc xử lý ngắt hoặc như vậy). Tôi nghĩ bạn có thể sử dụng từ khóa "dễ bay hơi" để buộc trình biên dịch cho rằng chúng có thể đã thay đổi. –
Để có thêm niềm vui, hãy xem điều gì xảy ra nếu bạn khai báo 'int * pf1 = &p->f1; int * pf2 = &p->f2;' vv và bây giờ thay thế 'p-> f1 ++' bằng '(* pf1) ++'. –
alexanius tự hỏi liệu tiêu chuẩn có loại trừ khả năng 'q' trỏ tới' p-> f2' hay không. Vì cấu trúc 't_str' chỉ có 2 thành viên' int', một mảng gồm 2 cấu trúc như vậy có thể được xem là có chứa một cấu trúc thứ ba như vậy tại địa chỉ '& p-> f2'.Tôi giả vờ rằng nó được loại trừ, nhưng nó có thể là khó khăn hơn để làm cho một trường hợp tương tự cho 'typedef struct {int f [2]; } t_str; ' – chqrlie