【问题标题】:Accessing different members of a c union simultaneously同时访问 c 联合的不同成员
【发布时间】:2017-01-18 13:44:10
【问题描述】:

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

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;

我在这里错过了什么?

注意:以上代码属于简单终端,终端仿真器。并且所有提到的数据结构都属于Xlib。

【问题讨论】:

  • 假设有一个 int(在原始的 union 中)重叠的结构与初始值是“安全的”(我的猜测是它不能保证是,但可能是“有效的” "),那么我认为XKeyEvent读取该值没有问题(大概KeyPressKeyReleaseXEvent 处理的两个更广泛的事件类型) .不过,struct { int type; union { .... } } 可能会更安全。
  • @TripeHound:除了你有两个types,一个在外部结构中,一个在union中的每个其他类型之前。似乎目标是让type 可读,而不管存储了哪个X*Event
  • @shadowranger 这正是重点 - X11 系统将事件作为不透明结构发送。如果有人想知道里面有什么,他可以检查type 成员并相应地强制转换联合以访问实际的数据成员。那是某种“C 中的 OOP”
  • 似乎这只是声明 typedef struct _XEvent{ int type; union { XAnyEvent xany; XKeyEvent xkey; XButtonEvent xbutton;} 的一种混淆方式,其中每个结构 XAnyEvent 等都没有 type 成员。
  • @Lundin 它使编码更容易,有时甚至更清晰 - 您可以通过 withwithout 访问事件类型内部结构。

标签: c struct unions xlib xorg


【解决方案1】:

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

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

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

X11 窗口系统将 XEvents 作为不透明结构(或在 OOP 术语中为“基类”)发送,接收者可以转换为原始结构(或在 OOP 中)术语“派生类”)根据事件类型。重叠的int 成员“type”用作类型选择器。

这样做是为了能够将通用代码中的事件“路由”到正确的位置,而无需处理每种事件类型。只有感兴趣的一方(实际的接收者)会将内部联合转换为正确的类型并“提取”它感兴趣的数据成员。

【讨论】:

  • 啊,我明白了。 handlerButtonPressButtonRelease 有不同的条目。也许应用程序处理按键和按键释放的方式不同。
  • 完全正确。 KeyPressKeyRelease,不过。但是按钮的作用是一样的。
【解决方案2】:

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

他们声明(我是在解释)最后写入的成员的值被重新解释为正在读取的成员的值(如果它们的大小不同,它的行为就像“人们期望的那样”)。该值可能是陷阱值,但允许进行简单的读取操作,而不是未定义的行为。

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

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

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

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

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

  1. 联合用于类型擦除。只有type 字段用于确定正确的处理程序。没有什么可以阻止在 handler 中注册多个 type 值的相同函数。

  2. 处理程序知道要查看哪个联合成员,因此它访问xkey。然后它会根据type 的值进行相应的操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    • 2019-08-12
    • 2023-03-31
    相关资源
    最近更新 更多