【问题标题】:Why is typecasting between to different structure pointers feasible?为什么在不同结构指针之间进行类型转换是可行的?
【发布时间】:2014-11-28 18:40:23
【问题描述】:

这段代码sn-p是从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 语言中进行这样的操作是一种好方法吗?请解释一下这是什么意思?

【问题讨论】:

    标签: c pointers xcb


    【解决方案1】:

    来自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,对我来说似乎是未定义的行为。

    【讨论】:

    • 是的,UB 是我在这个 XCB 结构定义中所关心的。
    【解决方案2】:

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

    我没有使用过 xcb,但只是看一下使用它的代码,我假设 xcb_wait_for_event(),它返回一个指向 xcb_generic_event_t 对象的指针,在这种情况下返回一个实际上指向 xcb_expose_event_t 的指针事件。顾名思义,前者是一种“通用”类型,可以用作几种更具体类型中的任何一种的占位符。前几个成员(包括response_type 成员)是共享的,因为它们具有相同的大小并且在两种结构类型中存储在相同的偏移量处。因此,代码可以安全地引用xcb_generic_event_t 对象的response_type 成员,并据此推断该对象实际上是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% 明确定义的,但在实践中它可以合理地确定是安全的。 (可以想象,一个激进的优化编译器可能会执行一些导致代码行为不端的转换,但这样的优化会破坏许多现有代码,编译器实现者极力避免这种情况。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-05
      相关资源
      最近更新 更多