为什么不同结构指针之间的类型转换是可行的?

时间:2014-10-03 17:04:45

标签: c pointers xcb

此代码段是从last tutorial example of the events in 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语言进行此类操作的好方法吗?请解释一下它的含义是什么?

2 个答案:

答案 0 :(得分: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的最后2个成员,这就不太可能造成问题。

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

  

为简化工会的使用,我们做出了一项特殊保证:   如果联合包含多个共享公共首字母的结构   序列(见下文),如果union对象当前包含一个   在这些结构中,允许检查共同的初始   任何一个声明已完成类型的部分   工会是可见的。两个结构共享一个公共首字母   序列如果相应的成员具有兼容的类型(并且,对于   比特字段,相同的宽度)用于一个或多个初始序列   成员。

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

答案 1 :(得分: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;

正如您所看到的,struct的第一个成员未被定义为xcb_generic_event_t,对我来说似乎是未定义的行为。