我正在寻找一些工会的例子,不是为了理解工会是如何工作的,希望我能做到,而是要看看人们对工会做了哪种黑客攻击。
所以随意分享你的工会黑客(当然有一些解释:))
答案 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;
这是一种访问单词部分的简便方法。 (一旦完成,平台的字节顺序的任何变化也可以轻松处理)