2010-11-16 23 views
14

Nếu tôi tuyên bố một liên minh như:Làm thế nào để biết được biến số nào của Union được sử dụng?

union TestUnion 
{ 
    struct 
    { 
     unsigned int Num; 
     unsigned char Name[5]; 
    }TestStruct; 
    unsigned char Total[7]; 
}; 

Bây giờ, Làm thế nào tôi có thể biết rằng cho dù Tổng số [7] được sử dụng hoặc TestStruct được sử dụng?

Tôi đang sử dụng C! Tôi đã xem xét lại các công đoàn và cấu trúc và câu hỏi này xuất hiện trong đầu tôi. Không thể sử dụng "sizeof" vì cả hai đều có cùng kích thước với 7 byte. (Và Ở đây có một câu hỏi khác)

Khi tôi chỉ điền "Tổng" với Ký tự 'a' và Đã thử sizeof(TestUnionInstance), nó trả về 12 (Kích thước của Char là 1 byte, Phải không?). Vì vậy, tôi cô lập cấu trúc từ nó và thấy rằng Kích thước của cấu trúc là 12 byte không 5 + 2 = 7 byte .... Lạ !! Mọi người đều có thể giải thích ??

P.S. Tôi đang sử dụng Visual Studio 2008.

Trả lời

20

Bạn không thể. Đó là một phần của quan điểm của các công đoàn.

Nếu bạn cần để có thể biết, bạn có thể sử dụng thứ được gọi là liên kết được gắn thẻ. Một số ngôn ngữ có hỗ trợ tích hợp cho những thứ này, nhưng trong C, bạn phải tự mình làm điều đó. Ý tưởng là để bao gồm một thẻ cùng với các công đoàn mà bạn có thể sử dụng để cho biết đó là phiên bản. Giống như:

enum TestUnionTag {NUM_NAME, TOTAL}; 

struct { 
    enum TestUnionTag tag; 
    union { 
     struct { 
      unsigned int Num; 
      unsigned char Name[5]; 
     } TestStruct; 
     unsigned char Total[7]; 
    } value; 
} TestUnion; 

Sau đó, trong mã của bạn, hãy đảm bảo bạn luôn đặt thẻ để cho biết cách kết hợp được sử dụng.

Về sizeof: cấu trúc là 12 byte vì có 4 byte cho int (trình biên dịch hiện đại nhất có int 4 byte, giống như int dài), sau đó ba byte đệm và năm byte cho chars (tôi không biết nếu padding đến trước hoặc sau khi ký tự). Các padding là có để cấu trúc là một số nguyên từ dài, để tất cả mọi thứ trong bộ nhớ vẫn liên kết trên ranh giới từ. Bởi vì cấu trúc dài 12 byte nên công đoàn phải dài 12 byte để giữ nó; công đoàn không thay đổi kích cỡ theo những gì trong đó.

+0

lời cảm ơn đặc biệt dành cho "Phần đệm" .... Tôi không biết điều này !! Tuyệt quá!! – Swanand

+2

Đệm sẽ xuất hiện sau 'char', không phải trước đó. Nói đúng một cách thực hiện * có thể * đặt đệm trước nó, nhưng sau đó nó cũng sẽ phải đặt cùng một lượng padding trước nó nếu bạn thay đổi từ 'char [5]' thành 'char [6]', nó sẽ khá vô nghĩa . C yêu cầu các cấu trúc với một chuỗi các phần tử ban đầu chung để tương thích. –

1

Không có cách nào để nói. Bạn nên có một số cờ bổ sung (hoặc các phương tiện khác bên ngoài công đoàn của bạn) nói rằng phần nào của các bộ phận công đoàn thực sự được sử dụng.

6

Thành viên sử dụng là thành viên bạn đã viết lần cuối; (các) khác là giới hạn. Bạn biết thành viên nào bạn đã viết cuối cùng phải không? Xét cho cùng, đó là bạn của những người viết chương trình :-)


Đối với bạn câu hỏi thứ: trình biên dịch được phép chèn 'đệm byte' trong cơ cấu để tránh truy cập unaligned và làm cho nó thêm performant .

example of a possible distribution of bytes inside your structure 

Num |Name  |pad 
- - - -|- - - - -|x x x 
0 1 2 3|4 5 6 7 8|9 a b 
+1

+1 để dành thời gian hiển thị bố cục bộ nhớ của đối tượng. –

+1

Thực ra, đôi khi bạn chỉ cần truy cập các thành viên khác. Một chức năng quan trọng của các công đoàn là cung cấp các quan điểm khác nhau cho cùng một dữ liệu. – thkala

+1

Đối với nhận xét "off-limits", cần lưu ý rằng theo như trình biên dịch thì không có gì là không có giới hạn. Lập trình viên phải tự thực thi bất kỳ chính sách nào như vậy. – thkala

2

Thứ nhất, sizeof(int) trên hầu hết các kiến ​​trúc ngày nay sẽ là 4. Nếu bạn muốn 2 bạn nên nhìn vào short, hoặc int16_t trong stdint.h tiêu đề trong C99 nếu bạn muốn trở thành cụ thể.

Thứ hai, C sử dụng byte đệm để đảm bảo mỗi struct được căn chỉnh với một từ-ranh giới (4). Vì vậy, cấu trúc của bạn trông giống như sau:

+---+---+---+---+---+---+---+---+---+---+---+---+ 
|  Num  | N a m e | | | | 
+---+---+---+---+---+---+---+---+---+---+---+---+ 

Có 3 byte ở cuối.Nếu không, struct tiếp theo trong một mảng sẽ có trường là Num ở vị trí được sắp xếp khó xử, điều này sẽ khiến việc truy cập trở nên kém hiệu quả hơn.

Thứ ba, sizeof một công đoàn sẽ là thành viên lớn nhất sizeof. Ngay cả khi tất cả không gian đó không được sử dụng, sizeof sẽ trả lại kết quả lớn nhất.

Bạn cần, như các câu trả lời khác đã đề cập, một số cách khác (như enum) để xác định trường nào của công đoàn bạn được sử dụng.

4

Câu trả lời ngắn gọn: không có cách nào ngoại trừ bằng cách thêm một enum ở đâu đó trong cấu trúc của bạn bên ngoài liên minh.

enum TestUnionPart 
{ 
    TUP_STRUCT, 
    TUP_TOTAL 
}; 

struct TestUnionStruct 
{ 
    enum TestUnionPart Part; 
    union 
    { 
    struct 
    { 
     unsigned int Num; 
     unsigned char Name[5]; 
    } TestStruct; 
    unsigned char Total[7]; 
    } TestUnion; 
}; 

Bây giờ bạn sẽ cần phải kiểm soát tạo ra các công đoàn của bạn để chắc chắn rằng các enum được thiết lập một cách chính xác, ví dụ với chức năng tương tự như:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts) 
{ 
    tus->Part = TUP_STRUCT; 
    memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts)); 
} 

văn trên các giá trị đúng bây giờ là một đơn switch:

void print(struct TestUnionStruct const * tus) 
{ 
    switch (tus->Part) 
    { 
    case TUP_STRUCT: 
     printf("Num = %u, Name = %s\n", 
      tus->TestUnion.TestStruct.Num, 
      tus->TestUnion.TestStruct.Name); 
     break; 
    case TUP_TOTAL: 
     printf("Total = %s\n", tus->TestUnion.Total); 
     break; 
    default: 
     /* Compiler can't make sure you'll never reach this case */ 
     assert(0); 
    } 
} 

Như một mặt lưu ý, tôi muốn đề cập đến các cấu trúc được xử lý tốt nhất d bằng ngôn ngữ của gia đình ML.

type test_struct = { num: int; name: string } 
type test_union = Struct of test_struct | Total of string 
Các vấn đề liên quan