Các ngữ nghĩa của các đối tượng chồng chéo trong C là gì?


25

Hãy xem xét các cấu trúc sau:

struct s {
  int a, b;
};

Thông thường 1 , cấu trúc này sẽ có kích thước 8 và căn 4.

Điều gì sẽ xảy ra nếu chúng ta tạo hai struct sđối tượng (chính xác hơn là chúng ta ghi vào lưu trữ được phân bổ hai đối tượng như vậy), với đối tượng thứ hai chồng lên đối tượng thứ nhất?

char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4

// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};

printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);

Có bất cứ điều gì về chương trình này không xác định hành vi? Nếu vậy, nó trở nên không xác định ở đâu? Nếu không phải là UB, có đảm bảo luôn in như sau:

o2.a=3
o2.b=4
o1.a=1
o1.b=3

Cụ thể, tôi muốn biết những gì xảy ra với đối tượng được chỉ vào o1khi nào o2, nó chồng lên nó, được viết. Nó vẫn được phép truy cập vào phần không bị che khuất ( o1->a)? Là truy cập vào phần bị ghi đè o1->bđơn giản giống như truy cập o2->a?

Làm thế nào để loại hiệu quả áp dụng ở đây? Các quy tắc đủ rõ ràng khi bạn nói về các đối tượng và con trỏ không chồng lấp chỉ đến cùng một vị trí với cửa hàng cuối cùng, nhưng khi bạn bắt đầu nói về loại phần hiệu quả của các đối tượng hoặc các đối tượng chồng chéo thì nó không rõ ràng.

Sẽ có gì thay đổi nếu lần viết thứ hai thuộc loại khác? Nếu các thành viên đã nói intshorthơn hai ints?

Đây là một cú hích nếu bạn muốn chơi với nó ở đó.


1 Câu trả lời này áp dụng cho các nền tảng không phải là trường hợp tương tự: ví dụ: một số có thể có kích thước 4 và căn chỉnh 2. Trên nền tảng có kích thước và căn chỉnh giống nhau, câu hỏi này sẽ không áp dụng vì các đối tượng chồng chéo, chồng chéo sẽ là không thể, nhưng tôi không chắc có nền tảng nào như vậy không.


2
Tôi khá chắc chắn đó là UB, nhưng tôi sẽ để một luật sư ngôn ngữ cung cấp chương và câu.
Barmar

Tôi nghĩ rằng trình biên dịch C trên các hệ thống vectơ Cray cũ buộc phải căn chỉnh và kích thước giống nhau, với mô hình ILP64 và căn chỉnh 64 bit bắt buộc (địa chỉ là các từ 64 bit - không có địa chỉ byte). Tất nhiên điều này tạo ra nhiều vấn đề khác ....
John D McCalpin

Câu trả lời:


15

Về cơ bản đây là tất cả các khu vực màu xám trong tiêu chuẩn; quy tắc răng cưa nghiêm ngặt chỉ định các trường hợp cơ bản và để người đọc (và nhà cung cấp trình biên dịch) điền vào các chi tiết.

Đã có những nỗ lực để viết một quy tắc tốt hơn nhưng cho đến nay họ vẫn chưa dẫn đến bất kỳ văn bản quy phạm nào và tôi không chắc tình trạng của điều này là gì đối với C2x.

Như đã đề cập trong câu trả lời của tôi cho câu hỏi trước đây của bạn, cách giải thích phổ biến nhất là p->qphương tiện (*p).qloại hiệu quả áp dụng cho tất cả *p, mặc dù sau đó chúng tôi tiếp tục áp dụng .q.

Theo cách giải thích này, printf("o1.a=%d\n", o1->a);sẽ gây ra hành vi không xác định vì loại vị trí hiệu quả*o1 là không s(vì một phần của nó đã bị ghi đè).

Lý do cho việc giải thích này có thể được nhìn thấy trong một chức năng như:

void f(s* s1, s* s2)
{
    s2->a = 5;
    s1->b = 6;
    printf("%d\n", s2->a);
}

Với cách giải thích này, dòng cuối cùng có thể được tối ưu hóa puts("5");, nhưng nếu không có nó, trình biên dịch sẽ phải xem xét rằng lệnh gọi hàm có thể đã bị f(o1, o2);và do đó mất tất cả các lợi ích được cung cấp theo quy tắc bí danh nghiêm ngặt.

Một đối số tương tự áp dụng cho hai loại cấu trúc không liên quan mà cả hai tình cờ có một intthành viên ở phần bù khác nhau.


1
Với f(s* s1, s* s2), không có restrict, trình biên dịch không thể giả định s1s2là các con trỏ khác nhau. Tôi nghĩ , một lần nữa mà không có restrict, thậm chí không thể cho rằng họ không chồng chéo một phần. IAC, tôi không thấy rằng mối quan tâm của OP là bản demo f()tương tự. Chúc may mắn không vướng bận. UV cho nửa đầu.
chux - Tái lập lại

@ chux-RebstateMonica mà không hạn chế, s1 == s2sẽ được cho phép, nhưng không được chồng chéo một phần. (Việc tối ưu hóa trong ví dụ mã của tôi vẫn có thể được thực hiện nếu s1 == s2)
MM

@ chux-ReinstateMonica bạn cũng có thể xem xét vấn đề tương tự chỉ intthay vì cấu trúc (và một hệ thống với _Alignof(int) < sizeof(int)).
MM

3
Tình trạng của loại câu hỏi này liên quan đến loại hiệu quả đối với C2x là khá nhiều mở và vẫn còn là chủ đề tranh luận trong nhóm nghiên cứu. Hãy cẩn thận mặc dù với yêu cầu tương đương p->q(*p).q. Điều này có thể đúng với loại tương tác như bạn nêu, nhưng nó không đúng theo quan điểm hoạt động. Điều quan trọng đối với truy cập đồng thời vào cùng một cấu trúc là quyền truy cập của thành viên không bao hàm quyền truy cập của bất kỳ thành viên nào khác.
Jens Gustyt

Quy tắc răng cưa nghiêm ngặt là về truy cập . Biểu thức phía bên trái trong E1.E2biểu thức không thực hiện quyền truy cập (ý tôi là toàn bộ E1biểu thức. Một số biểu thức con của nó có thể thực hiện quyền truy cập. Tức E1là nếu (*p)đọc giá trị con trỏ khi đánh giá plà quyền truy cập, nhưng đánh giá *phoặc (*p)không thực hiện bất kỳ truy cập). Quy tắc răng cưa nghiêm ngặt không áp dụng trong trường hợp khi không có quyền truy cập.
Luật sư ngôn ngữ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.