3

此代码片段是从XCB 中事件的最后一个教程示例复制而来的:

01    xcb_generic_event_t *event;
02    while ( (event = xcb_wait_for_event (connection)) ) {
03        switch (event->response_type & ~0x80) {
04        case XCB_EXPOSE: {
05            xcb_expose_event_t *expose = (xcb_expose_event_t *)event;
06            printf ("Window %"PRIu32" exposed. Region to be redrawn at location (%"PRIu16",%"PRIu16"), with dimension (%"PRIu16",%"PRIu16")\n",
07                    expose->window, expose->x, expose->y, expose->width, expose->height );
08            break;
09        }

在第 5 行中,指针xcb_generic_event_t被类型转换为指针xcb_expose_event_t,这是在标准 C 语言中进行此类操作的好方法吗?并请解释它的含义是什么?

4

2 回答 2

5

这是可行的,因为两个结构都以相同的少数成员开始。

我没有使用过 xcb,但只是查看使用它的代码,我认为xcb_wait_for_event()它返回一个指向xcb_generic_event_t对象的指针,在这种情况下返回一个实际指向xcb_expose_event_t事件的指针。顾名思义,前者是一种“通用”类型,可以用作几种更具体类型中的任何一种的占位符。前几个成员(包括response_type成员)是共享的,因为它们具有相同的大小并且在两种结构类型中存储在相同的偏移量处。因此,代码可以安全地引用对象的response_type成员xcb_generic_event_t,并据此推断该对象实际上是一个xcb_expose_event_t对象。指针转换允许代码将对象重新解释为xcb_expose_event_t对象。

查看这两种类型的链接定义,我看到xcb_generic_event_t实际上有 5 个成员,并且只有前 3 个与xcb_expose_event_t. 只要代码不引用xcb_generic_event_t.

C 标准对这种情况做出了特殊保证。引用N1570 6.5.3.2,第 6 段:

一个特殊的保证是为了简化联合的使用:如果一个联合包含多个共享一个公共初始序列的结构(见下文),并且如果联合对象当前包含这些结构之一,则允许检查公共它们中的任何一个的初始部分,在任何地方都可以看到已完成联合类型的声明。如果对应的成员对于一个或多个初始成员的序列具有兼容的类型(并且对于位域,具有相同的宽度),则两个结构共享一个共同的初始序列。

严格来说,这仅适用于两个结构是联合体的成员时。但是 C 编译器满足此保证的最简单方法是为具有公共初始子序列的所有结构提供该子序列的相同布局。如果问题中的代码的行为可能不是 100% 明确定义的,但在实践中它可以合理地确定是安全的。(可以想象,一个激进的优化编译器可能会执行一些导致代码行为异常的转换,但这样的优化会破坏许多现有代码,编译器实现者极力避免这种情况。)

于 2014-10-03T17:56:07.340 回答
2

来自C 编程语言 - 第二版

A.8.3 结构和联合声明

如果指向结构的指针转换为指向其第一个成员的指针的类型,则结果引用第一个成员。

但在这种情况下xcb_expose_event_t定义为

typedef struct {
    uint8_t      response_type; /* The type of the event, here it is XCB_EXPOSE */
    uint8_t      pad0;
    uint16_t     sequence;
    xcb_window_t window;        /* The Id of the window that receives the event (in case */
                                /* our application registered for events on several windows */
    uint16_t     x;             /* The x coordinate of the top-left part of the window that needs to be redrawn */
    uint16_t     y;             /* The y coordinate of the top-left part of the window that needs to be redrawn */
    uint16_t     width;         /* The width of the part of the window that needs to be redrawn */
    uint16_t     height;        /* The height of the part of the window that needs to be redrawn */
    uint16_t     count;
} xcb_expose_event_t;

如您所见,第一个成员未定义structxcb_generic_event_t,对我来说似乎是未定义的行为。

于 2014-10-03T17:09:01.373 回答