2009-04-07 41 views
31

Tôi đang tìm một số ví dụ công đoàn, không hiểu cách thức hoạt động của công đoàn, hy vọng tôi làm, nhưng để xem loại người nào làm việc với công đoàn.Ví dụ về Liên minh trong C

Vì vậy, cảm thấy tự do để chia sẻ công đoàn của bạn hack (với một số giải thích tất nhiên :))

Trả lời

34

Một cổ điển là đại diện cho một giá trị kiểu "không rõ", như trong cốt lõi của một máy ảo đơn giản:

typedef enum { INTEGER, STRING, REAL, POINTER } Type; 

typedef struct 
{ 
    Type type; 
    union { 
    int integer; 
    char *string; 
    float real; 
    void *pointer; 
    } x; 
} Value; 

Sử dụng này, bạn có thể viết mã để xử lý "giá trị" mà không biết loại chính xác của họ , ví dụ thực hiện một ngăn xếp và như vậy.

Vì đây là trong (cũ, trước C11) C, công đoàn phải được đặt tên trường ở bên ngoài struct. Trong C++, bạn có thể cho phép union ẩn danh. Chọn tên này có thể khó. Tôi có xu hướng đi với một cái gì đó đơn chữ, vì nó gần như không bao giờ được tham chiếu trong sự cô lập và do đó nó luôn luôn rõ ràng từ bối cảnh những gì đang xảy ra.

Mã để đặt một giá trị cho một số nguyên có thể trông như thế này:

Value value_new_integer(int v) 
{ 
    Value v; 
    v.type = INTEGER; 
    v.x.integer = v; 
    return v; 
} 

Ở đây tôi sử dụng thực tế là struct s có thể được trả trực tiếp, và điều trị gần giống như giá trị của một loại nguyên thủy (bạn có thể gán struct s).

+0

Tôi đoán cái này có thể hữu ích! – claf

+1

Lưu ý rằng C11 cung cấp các công đoàn ẩn danh, vì vậy 'vì đây là đoạn C' áp dụng cho C90 và C99, nhưng không áp dụng cho C11.Tương tự như vậy, kể từ khi câu trả lời này được viết vào năm 2009, nó là khá hợp lý để không dự đoán những gì C11 sẽ cung cấp. –

+1

@unwind vì tò mò, tôi hỏi câu hỏi này. Hàm trên không cung cấp _redeclaration error_? –

9

Dưới đây là một chút tôi sử dụng mỗi ngày:

struct tagVARIANT { 
    union { 
     struct __tagVARIANT { 
      VARTYPE vt; 
      WORD wReserved1; 
      WORD wReserved2; 
      WORD wReserved3; 
      union { 
       LONG   lVal;   /* VT_I4    */ 
       BYTE   bVal;   /* VT_UI1    */ 
       SHORT   iVal;   /* VT_I2    */ 
       FLOAT   fltVal;  /* VT_R4    */ 
       DOUBLE  dblVal;  /* VT_R8    */ 
       VARIANT_BOOL boolVal;  /* VT_BOOL    */ 
       _VARIANT_BOOL bool;   /* (obsolete)   */ 
       SCODE   scode;  /* VT_ERROR    */ 
       CY   cyVal;  /* VT_CY    */ 
       DATE   date;   /* VT_DATE    */ 
       BSTR   bstrVal;  /* VT_BSTR    */ 
       IUnknown * punkVal;  /* VT_UNKNOWN   */ 
       IDispatch * pdispVal;  /* VT_DISPATCH   */ 
       SAFEARRAY * parray;  /* VT_ARRAY    */ 
       BYTE *  pbVal;  /* VT_BYREF|VT_UI1  */ 
       SHORT *  piVal;  /* VT_BYREF|VT_I2  */ 
       LONG *  plVal;  /* VT_BYREF|VT_I4  */ 
       FLOAT *  pfltVal;  /* VT_BYREF|VT_R4  */ 
       DOUBLE *  pdblVal;  /* VT_BYREF|VT_R8  */ 
       VARIANT_BOOL *pboolVal;  /* VT_BYREF|VT_BOOL  */ 
       SCODE *  pscode;  /* VT_BYREF|VT_ERROR */ 
       CY *   pcyVal;  /* VT_BYREF|VT_CY  */ 
       DATE *  pdate;  /* VT_BYREF|VT_DATE  */ 
       BSTR *  pbstrVal;  /* VT_BYREF|VT_BSTR  */ 
       IUnknown ** ppunkVal;  /* VT_BYREF|VT_UNKNOWN */ 
       IDispatch ** ppdispVal; /* VT_BYREF|VT_DISPATCH */ 
       SAFEARRAY ** pparray;  /* VT_BYREF|VT_ARRAY */ 
       VARIANT *  pvarVal;  /* VT_BYREF|VT_VARIANT */ 
       PVOID   byref;  /* Generic ByRef  */ 
       CHAR   cVal;   /* VT_I1    */ 
       USHORT  uiVal;  /* VT_UI2    */ 
       ULONG   ulVal;  /* VT_UI4    */ 
       INT   intVal;  /* VT_INT    */ 
       UINT   uintVal;  /* VT_UINT    */ 
       DECIMAL *  pdecVal;  /* VT_BYREF|VT_DECIMAL */ 
       CHAR *  pcVal;  /* VT_BYREF|VT_I1  */ 
       USHORT *  puiVal;  /* VT_BYREF|VT_UI2  */ 
       ULONG *  pulVal;  /* VT_BYREF|VT_UI4  */ 
       INT *   pintVal;  /* VT_BYREF|VT_INT  */ 
       UINT *  puintVal;  /* VT_BYREF|VT_UINT  */ 
      } __VARIANT_NAME_3; 
     } __VARIANT_NAME_2; 
     DECIMAL decVal; 
    } __VARIANT_NAME_1; 
}; 

Đây là định nghĩa của các biến thể tự động hóa OLE loại dữ liệu. Như bạn có thể thấy nó có rất nhiều loại có thể. Có rất nhiều quy tắc xung quanh các loại bạn có thể sử dụng trong các tình huống khác nhau, tùy thuộc vào khả năng của mã khách hàng dự định của bạn. Không phải tất cả các loại đều được hỗ trợ bởi tất cả các ngôn ngữ.

Các loại với VT_BYREF sau khi chúng được sử dụng bởi các ngôn ngữ như VBScript chuyển tham số theo tham chiếu theo mặc định. Điều này có nghĩa là nếu bạn có một số mã quan tâm đến các chi tiết cấu trúc biến thể (như C++) được gọi bằng mã không (chẳng hạn như VB), thì bạn phải cẩn thận dereference tham số biến thể nếu được yêu cầu.

Các loại byref cũng được sử dụng để trả về các giá trị từ các hàm. Ngoài ra còn có hỗ trợ cho các loại mảng bằng cách sử dụng loại SAFEARRAY sai tên lạ - rất khó sử dụng từ C++.

Nếu bạn có một chuỗi các chuỗi, bạn có thể chuyển nó vào vbscript, nhưng nó không thể được sử dụng (ngoại trừ để in kích thước). Để thực sự đọc các giá trị, dữ liệu mảng cần phải thuộc loại VT_BYREF | VT_BSTR.

+31

OMG, mắt tôi đang chảy máu! – paxdiablo

+0

Giải thích ở đâu? :-) –

+0

Tôi nghĩ rằng đó là tự giải thích :) Đây là định nghĩa của kiểu dữ liệu biến thể tự động hóa OLE. Như bạn có thể thấy nó có rất nhiều loại có thể. Không phải tất cả các loại đều được hỗ trợ bởi tất cả các ngôn ngữ –

2

Thật trùng hợp, tôi chỉ sử dụng một câu trả lời trong câu trả lời Stackoverflow here vì vậy tôi có thể xử lý một từ được tạo thành từ các trường 6 bit dưới dạng hai số nguyên không dấu 16 bit.

Năm trước, tôi cũng sử dụng một trình biên dịch ARM C đầu tiên - hướng dẫn trong những ngày đó là 32 bit, nhưng có bố trí khác nhau tùy thuộc vào hướng dẫn chính xác. Vì vậy, tôi đã có một liên minh để đại diện cho một lệnh ARM, có chứa một tập hợp các cấu trúc mà mỗi trường có các bitfield thích hợp cho một loại lệnh cụ thể.

+0

Điều ARM trông đẹp và xấu xí :) – claf

+0

Đó là :) Nhưng (trong một câu chuyện cảnh báo về tính di động của những điều này) nó bật ra tôi đã nhận tất cả các bit theo thứ tự sai trong lần đầu tiên ... –

3
struct InputEvent 
{ 
    enum EventType 
    { 
     EventKeyPressed, 
     EventKeyPressRepeated, 
     EventKeyReleased, 
     EventMousePressed, 
     EventMouseMoved, 
     EventMouseReleased 
    } Type; 
    union 
    { 
     unsigned int KeyCode; 
     struct 
     { 
      int x; 
      int y; 
      unsigned int ButtonCode; 
     }; 
    }; 
}; 
... 
std::vector<InputEvent> InputQueue; 

bằng công đoàn hack Tôi chỉ đơn giản có thể tạo một vectơ vật thể. Tôi chắc rằng điều này có thể được làm sạch hơn ...nhưng nó hoạt động cho tôi - KISS

+0

SDL sử dụng cấu trúc/sự kiện tương tự. – aib

6

Hãy tránh "hack" với công đoàn, chúng gây ra sự đau đầu về tính di động (tính cuối cùng, vấn đề căn chỉnh).

  • Sử dụng hợp pháp công đoàn là lưu trữ các loại dữ liệu khác nhau tại cùng một nơi, tốt nhất là với thẻ để bạn biết loại đó là gì. Xem ví dụ bằng 1800 THÔNG TIN.

  • Không sử dụng công đoàn để chuyển đổi giữa các loại dữ liệu, ví dụ: từ một số nguyên đến vài byte. Thay vào đó, hãy sử dụng thay đổi và mặt nạ cho tính di động.

+0

Một số công đoàn (xem pthread.h trên linux) giúp tính di động không bạn nghĩ như vậy? – claf

+1

Không có liên kết nào trong các tệp pthread.h trên hệ thống của tôi (tất cả 48 tệp). – starblue

+0

xem nguồn nptl trong glibc – claf

1
#define DWORD unsigned int 
#define WORD unsigned short 
#define BYTE unsigned char 

typedef union _DWORD_PART_ { 

    DWORD dwWord; 

    struct { 
     WORD dwMSB; 
     WORD dwLSB; 
    }hw; 

    struct { 

     BYTE byMSB; 
     BYTE byMSBL; 
     BYTE byLSBH; 
     BYTE byLSB; 

    } b; 

} DWORD_PART; 

Đây là một cách dễ dàng để truy cập vào phần lời. (Một khi bạn đã hoàn thành, mọi thay đổi về tính cuối cùng của nền tảng cũng có thể được xử lý dễ dàng)

+1

Lưu ý rằng điều này phụ thuộc vào hành vi được xác định thực hiện. Nó chỉ là di động cho các nền tảng mà việc thực hiện xác định rằng nó sẽ có ngữ nghĩa bạn dự định. Nó là hợp pháp đối với một nền tảng để không chồng chéo lưu trữ của các thành viên của một công đoàn, ví dụ, miễn là nó ghi lại nó. – RBerteig

+1

Tôi không hiểu khi nào bạn nói điều này sẽ không hoạt động. Nó hoạt động trong Windows, Linux, ARM, MacOS và PPC. Không một lần tôi đã thấy nó thất bại. Dù sao, tôi nghĩ rằng điều này có thể hữu ích, nếu bạn không muốn sau đó SU. – Alphaneo

+2

-1 Điều này sai đối với các máy cuối nhỏ (ví dụ x86). Vui lòng không sử dụng công đoàn để chuyển đổi giữa các từ và byte của máy, điều này là xấu đối với tính di động. – starblue

8

Các công đoàn cũng thường được sử dụng trong phân tích từ vựng và phân tích cú pháp của bộ xử lý ngôn ngữ, như trình biên dịch và thông dịch viên. Đây là một trong những tôi đang chỉnh sửa ngay bây giờ.

union { 
    char c; 
    int i; 
    string *s; 
    double d; 
    Expression *e; 
    ExpressionList *el; 
    fpos_t fp; 
} 

Công đoàn được sử dụng để kết hợp các giá trị ngữ nghĩa với mã thông báo của trình phân tích cú pháp và sản phẩm của trình phân tích cú pháp. Thực tiễn này khá phổ biến trong các trình tạo ngữ pháp, chẳng hạn như yacc, cung cấp hỗ trợ rõ ràng cho nó. Công đoàn có thể giữ bất kỳ giá trị nào của nó, nhưng chỉ có một trong số đó vào thời điểm đó. Ví dụ, tại bất kỳ điểm nào từ tệp đầu vào bạn đã đọc một hằng số ký tự (được lưu trữ trong c) hoặc số nguyên (được lưu trữ trong i) hoặc số dấu phẩy động (được lưu trữ trong d). Trình tạo ngữ pháp cung cấp hỗ trợ đáng kể để xác định giá trị nào được lưu trữ tại một thời điểm tùy thuộc vào quy tắc đang được xử lý.

3

Chúng tôi sử dụng công đoàn cho thư đóng gói tại nơi làm việc (C/C++), vì vậy chúng tôi có thể truyền xung quanh một cấu trúc với công đoàn làm thành viên dữ liệu, sau đó truy cập đường dẫn chính xác dựa trên trường id trong cấu trúc.

Làm việc tìm cho đến khi ai đó đã viết cấu trúc vào một tập tin, bây giờ chúng tôi đang giới hạn ở những dữ liệu lớn nhất được sử dụng trong các tập tin, bởi vì thậm chí còn nghĩ có một phiên bản tập tin, không ai thay đổi nó ....

Vì vậy, trong khi hữu ích cho công việc trong bộ nhớ, tránh mù quáng viết chúng vào đĩa hoặc mạng.