同时访问一个 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
不是结构,但标准说明了另外两件事:
- 经过适当转换的结构指针指向第一个成员。
- 经过适当转换的联合指针指向任何联合成员。
因此每个结构中的每个 type
必须驻留在与 XEvent
中的 type
相同的内存位置。因此,无论您正在阅读 ev.type
还是 ev.xkey.type
,它都是相同的 int.
那么您的代码中发生了什么:
联合用于类型擦除。只有 type
字段用于确定正确的处理程序。 type
.
的多个值不排除在 handler
中注册相同的函数
处理程序知道要查看哪个联合成员,因此它访问 xkey
。然后根据type
.
的值进行相应的操作
我花了一段时间才明白你认为你的问题在哪里 - 因为实际上,none...
您似乎假设事件类型 int 和联合中有效的结构之间必须存在 1:1 关系。不,没有。
Xlib 所做的是:它将为 KeyPress 和 KeyRelease 事件放置相同的 XKeyEvent 结构(它们使用相同的数据成员,因此可用于两种事件情况)。
X11 窗口系统将 XEvent 作为 不透明结构(或 OOP 术语"base classes")发送,接收者可以根据事件类型转换为原始结构(或 OOP 术语 "derived classes")。重叠 int
成员 "type" 用作类型选择器。
这样做是为了能够 "route" 通用代码中的事件到正确的位置,而不必处理每种事件类型。只有感兴趣的一方(实际接收者)会将内部联合转换为正确的类型和 "extract" 它感兴趣的数据成员。
我遇到过一段代码,它似乎同时使用了联合的不同成员:
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
不是结构,但标准说明了另外两件事:
- 经过适当转换的结构指针指向第一个成员。
- 经过适当转换的联合指针指向任何联合成员。
因此每个结构中的每个 type
必须驻留在与 XEvent
中的 type
相同的内存位置。因此,无论您正在阅读 ev.type
还是 ev.xkey.type
,它都是相同的 int.
那么您的代码中发生了什么:
联合用于类型擦除。只有
type
字段用于确定正确的处理程序。type
. 的多个值不排除在 处理程序知道要查看哪个联合成员,因此它访问
xkey
。然后根据type
. 的值进行相应的操作
handler
中注册相同的函数
我花了一段时间才明白你认为你的问题在哪里 - 因为实际上,none...
您似乎假设事件类型 int 和联合中有效的结构之间必须存在 1:1 关系。不,没有。
Xlib 所做的是:它将为 KeyPress 和 KeyRelease 事件放置相同的 XKeyEvent 结构(它们使用相同的数据成员,因此可用于两种事件情况)。
X11 窗口系统将 XEvent 作为 不透明结构(或 OOP 术语"base classes")发送,接收者可以根据事件类型转换为原始结构(或 OOP 术语 "derived classes")。重叠 int
成员 "type" 用作类型选择器。
这样做是为了能够 "route" 通用代码中的事件到正确的位置,而不必处理每种事件类型。只有感兴趣的一方(实际接收者)会将内部联合转换为正确的类型和 "extract" 它感兴趣的数据成员。