同时访问一个 c union 的不同成员

Accessing different members of a c union simultaneously

我遇到过一段代码,它似乎同时使用了联合的不同成员:

XEvent ev;

if(handler[ev.type])
    (handler[ev.type])(&ev);

Handler 是一个函数数组。下面是 XEvent 的定义:

typedef union _XEvent{
    int type;/*must not be changed*/
    XAnyEvent xany;
    XKeyEvent xkey;
    XButtonEvent xbutton;
............
} Xevent;

XEvent 的所有结构成员都有一个 int 作为第一个成员。被调用函数使用XEvent的适当成员结构。

void
kpress(XEvent *ev) {
    XKeyEvent *e = &ev->xkey;

问题是 XKeyEvent 似乎也使用其第一个 int 成员的值来确定事件是按键按下还是按键释放。

typedef struct {
    int type; /* KeyPress or KeyRelease */
    unsigned long serial;   /* # of last request processed by server */
    .............
} XKeyEvent;

我在这里错过了什么?

注:以上代码属于simple terminal,一个终端模拟器。并且提到的所有数据结构都属于Xlib。

C99(以前的 C 标准)和现在的 C11,允许读取联合成员,即使它不是最后写入的成员。

他们声明(我的意思是)最后写入的成员的值被重新解释为正在读取的成员的值(如果它们的大小不同,则表现为 "one would expect")。该值可能是一个陷阱值,但允许简单的读取行为,而不是未定义的行为。

现在,该标准还规定,如果一个联合包含多个共享相同初始序列的成员结构,您可以通过任何联合成员检查公共序列。

type in XEvent 不是结构,但标准说明了另外两件事:

  1. 经过适当转换的结构指针指向第一个成员。
  2. 经过适当转换的联合指针指向任何联合成员。

因此每个结构中的每个 type 必须驻留在与 XEvent 中的 type 相同的内存位置。因此,无论您正在阅读 ev.type 还是 ev.xkey.type,它都是相同的 int.

那么您的代码中发生了什么:

  1. 联合用于类型擦除。只有 type 字段用于确定正确的处理程序。 type.

  2. 的多个值不排除在 handler 中注册相同的函数
  3. 处理程序知道要查看哪个联合成员,因此它访问 xkey。然后根据type.

  4. 的值进行相应的操作

我花了一段时间才明白你认为你的问题在哪里 - 因为实际上,none...

您似乎假设事件类型 int 和联合中有效的结构之间必须存在 1:1 关系。不,没有。

Xlib 所做的是:它将为 KeyPress 和 KeyRelease 事件放置相同的 XKeyEvent 结构(它们使用相同的数据成员,因此可用于两种事件情况)。

X11 窗口系统将 XEvent 作为 不透明结构(或 OOP 术语"base classes")发送,接收者可以根据事件类型转换为原始结构(或 OOP 术语 "derived classes")。重叠 int 成员 "type" 用作类型选择器。

这样做是为了能够 "route" 通用代码中的事件到正确的位置,而不必处理每种事件类型。只有感兴趣的一方(实际接收者)会将内部联合转换为正确的类型和 "extract" 它感兴趣的数据成员。