C中的联盟的例子

时间:2009-04-07 08:15:17

标签: c unions

我正在寻找一些工会的例子,不是为了理解工会是如何工作的,希望我能做到,而是要看看人们对工会做了哪种黑客攻击。

所以随意分享你的工会黑客(当然有一些解释:))

8 个答案:

答案 0 :(得分:35)

一个经典之处是表示“未知”类型的值,就像在简单虚拟机的核心中一样:

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

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

使用它可以编写处理“值”的代码,而无需知道它们的确切类型,例如实现堆栈等。

由于这是在(旧的,C11之前的)C中,因此必须在外部struct中为内部联合赋予字段名称。在C ++中,您可以让union匿名。选择这个名字可能很难。我倾向于选择单字母,因为它几乎从不单独引用,因此从上下文中总是清楚地知道发生了什么。

将值设置为整数的代码可能如下所示:

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

这里我使用的事实是struct s可以直接返回,并且几乎像原始类型的值一样(您可以指定struct s)。

答案 1 :(得分:10)

这是我每天使用的一小部分:

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;
};

这是OLE自动化变体数据类型的定义。如您所见,它有很多可能的类型。根据您预期的客户端代码的功能,您可以在不同情况下使用的类型有很多规则。并非所有类型都支持所有类型。

其后带有VT_BYREF的类型由VBScript等语言使用,默认情况下通过引用传递参数。这意味着如果你有一些代码关注变量结构细节(例如C ++)被没有的代码调用(例如VB),那么你必须在必要时小心地取消引用变量参数。

byref类型也用于从函数返回值。还支持使用奇怪的错误名称SAFEARRAY类型的数组类型 - 很难在C ++中使用。

如果你有一个字符串数组,你可以将它传递给vbscript,但不能使用它(打印大小除外)。要实际读取值,数组数据必须是VT_BYREF | VT_BSTR类型。

答案 2 :(得分:8)

工会也常用于语言处理器的词法分析和解析阶段,如编译器和解释器。这是我现在正在编辑的一个。

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

union用于将语义值与词法分析器的标记和解析器的产生相关联。这种做法在语法生成器中很常见,例如 yacc ,它为它提供了明确的支持。工会可以保留其任何价值,但当时只有其中一个。例如,在输入文件的任何一点,您要么读取字符常量(存储在c中),要么读取整数(存储在i中)或浮点数(存储在{ {1}})。语法生成器为确定在任何时间存储哪些值提供了相当大的帮助,具体取决于正在处理的规则。

答案 3 :(得分:7)

请避免使用union进行“hacks”,它们会导致可移植性问题(字节序,对齐问题)。

  • union的合法用法是在同一个地方存储不同的数据类型,最好使用标记,以便您知道它是哪种类型。请参阅1800 INFORMATION的示例。

  • 不要使用union在数据类型之间进行转换,例如从一个整数到几个字节。使用shift和masking代替可移植性。

答案 4 :(得分:4)

我们在工作时使用联合打包消息(C / C ++),因此我们可以传递一个带有union作为数据成员的结构,然后根据结构中的id字段访问正确的路径。

工作正常,直到有人将结构写入文件,现在我们仅限于文件中使用的最大数据,因为即使有文件版本,也没有人改变它....

因此,虽然对于内存工作很有用,但应避免盲目地将它们写入磁盘或网络。

答案 5 :(得分: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;

使用union hack我可以简单地创建一个对象向量。我相信这可以变得更干净......但它对我有用 - KISS

答案 6 :(得分:2)

巧合的是,我刚刚在Stackoverflow中使用了一个答案here,所以我可以将一个由6位字段组成的单词视为两个16位无符号整数。

多年前,我还使用了一个(第一个)ARM C编译器 - 当时的指令都是32位,但根据确切的指令有不同的布局。所以我有一个表示ARM指令的联合,它包含一组结构,每个结构都有一个特定指令类型的相应位域。

答案 7 :(得分: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;

这是一种访问单词部分的简便方法。 (一旦完成,平台的字节顺序的任何变化也可以轻松处理)