2012-01-02 38 views
7

Giả sử chúng ta có hai cấu trúc:Struct con trỏ tương thích

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

Có an toàn để đúc Struct2 *-Struct1 *? Thông số ANSI nói gì về điều này? Tôi biết rằng một số trình biên dịch có tùy chọn sắp xếp lại các trường cấu trúc để tối ưu hóa việc sử dụng bộ nhớ, điều này có thể khiến cho hai cấu trúc không tương thích. Có cách nào để chắc chắn mã này sẽ hợp lệ, bất kể cờ trình biên dịch?

Cảm ơn bạn!

+2

* Sắp xếp lại * các thành viên không được AFAIK chuẩn cho phép. Tôi tin rằng chèn số lượng khác nhau của padding sẽ được cho phép mặc dù. – delnan

+0

@delnan Oh vậy thì cấu trúc 'đóng gói' sẽ chỉ tắt liên kết? Cảm ơn, tôi không biết điều đó! – Waneck

Trả lời

5

struct con trỏ loại luôn luôn có các đại diện như nhau trong C.

(C99, 6.2.5p27) "Tất cả các con trỏ đến cấu trúc loại sẽ có cùng yêu cầu đại diện và liên kết như nhau."

Và các thành viên trong các loại cấu trúc luôn trong trật tự trong C.

(C99, 6.7.2.1p5) "một cấu trúc là một loại bao gồm một chuỗi các thành viên, có lưu trữ được phân bổ trong một chuỗi được sắp xếp theo thứ tự "

+1

Điều này không trả lời được câu hỏi; ngay cả với những ràng buộc này, nó vẫn có thể là một sự vi phạm bí danh. Tuy nhiên, trong những điều kiện nhất định, tiêu chuẩn C cho phép rõ ràng những gì OP muốn. –

+0

Cảm ơn bạn rất nhiều vì những trích dẫn từ thông số ANSI. Điều này cho tôi làm cho nó rõ ràng rằng điều này là an toàn! – Waneck

+0

@R. Những điều kiện nào? – Waneck

2

Rất có thể nó sẽ hoạt động. Nhưng bạn rất chính xác trong việc hỏi làm thế nào bạn có thể chắc chắn mã này sẽ hợp lệ. Vì vậy: một nơi nào đó trong chương trình của bạn (lúc khởi động) có thể nhúng một loạt các câu lệnh ASSERT, đảm bảo rằng offsetof(Struct1.a_short) bằng offsetof(Struct2.a_short) v.v ... Bên cạnh đó, một số lập trình viên khác có thể sửa đổi một trong những cấu trúc này. an toàn hơn xin lỗi.

+0

Sử dụng xác nhận tĩnh sẽ tốt hơn nhiều ... –

+0

Cảm ơn Mike, tôi chắc chắn sẽ thêm một số xác nhận để đảm bảo điều này! – Waneck

+0

@R .. xác nhận tĩnh? Tôi không biết họ đã tồn tại.[Tôi đã tra cứu và phát hiện ra] (http://stackoverflow.com/questions/3385515/static-assert-in-c). Bạn nói đúng, cảm ơn. –

3

Nó an toàn, theo như tôi biết.

Nhưng nó tốt hơn nhiều, nếu có thể, để làm:

typedef struct { 
    Struct1 struct1; 
    short another_short; 
} Struct2; 

Sau đó, bạn thậm chí đã nói với trình biên dịch rằng Struct2 bắt đầu với một thể hiện của Struct1, và vì một con trỏ đến một cấu trúc luôn trỏ vào nó thành viên đầu tiên, bạn an toàn để xử lý Struct2 * dưới dạng Struct1 *.

+0

, nếu có cơ hội nhỏ nhất một ngày 'offsetof (Struct1.a_short)' sẽ được tìm thấy KHÔNG bằng với 'offsetof (Struct2.a_short)' thì có một số lượng cơ hội bằng nhau mà một ngày 'offsetof (Struct2.struct1)' sẽ được tìm thấy không bằng 0. (Điều đó có nghĩa là '& struct2! = (Struct2 *) & struct2.struct1'). –

+0

Thật vậy, cách này tốt hơn nhiều! :) Cảm ơn bạn! – Waneck

+0

Nếu struct1 và struct2 đều đặt liên kết 32 bit "int" đầu tiên và "int", thì cả hai loại cấu trúc đều có thể là 8 byte, nhưng dạng thay thế của Struct2 sẽ yêu cầu 12 byte. Nếu các trình biên dịch tôn trọng quy tắc Chuỗi ban đầu chung, một trong hai biểu mẫu phải hợp lệ (và biểu mẫu 8 byte sẽ hiệu quả hơn), nhưng ngay cả khi được gọi trong chế độ C89, gcc không còn duy trì đảm bảo của C89 trừ khi '-fno-strict- cờ aliasing' được sử dụng. – supercat

1

Có, nó là ok để làm điều đó!

Chương trình mẫu như sau.

#include <stdio.h> 

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

int main(void) 
{ 

    Struct2 s2 = {1, 2, 3}; 
    Struct1 *ptr = &s2; 
    void *vp = &s2; 
    Struct1 *s1ptr = (Struct1 *)vp; 

    printf("%d, %d \n", ptr->a_short, ptr->id); 
    printf("%d, %d \n", s1ptr->a_short, s1ptr->id); 

    return 0; 
} 
Các vấn đề liên quan