在C11匿名结构定义中使用宏

4
典型的 C99 扩展结构的方法如下:
struct Base {
    int x;
    /* ... */
};

struct Derived {
    struct Base base_part;
    int y;
    /* ... */
};

然后我们可以将 struct Derived * 的实例强制转换为 struct Base *,然后访问 x

我想直接访问 struct Derived * obj; 的基本元素,例如 obj->x 和 obj->y。C11 提供了扩展结构体,但正如这里所解释的那样,我们只能在匿名定义中使用此特性。那么怎么写呢?

#define BASE_BODY { \
    int x; \
}

struct Base BASE_BODY;

struct Derived {
    struct BASE_BODY;
    int y;
};

那么我可以像操作派生类的一部分一样访问基类成员,无需任何转换或中间成员。如果需要,我可以将Derived指针强制转换为Base指针。

这种方法可行吗?有什么要注意的吗?


1
作为替代方案,您可以考虑在 gccclang 中使用 -fms-extensions 选项通过 typedef(请参见答案)来实现,但这是非标准的。 - Grzegorz Szpetkowski
1个回答

2
有一些需要注意的地方。
考虑以下内容:
#define BASE_BODY { \
    double a; \
    short b; \
}

struct Base BASE_BODY;

struct Derived {
    struct BASE_BODY;
    short c;
};

在某些实现中,sizeof(Base) == sizeof(Derived),但是:


这并不总是适用。
struct Base {
    double a;
    // Padding here
    short b;
}

struct Derived {
    double a;
    short b;
    short c;
};

不能保证结构体内存布局的开头相同。因此,您不能将这种Derived *传递给期望Base *的函数,并期望它正常工作。

即使填充不会混乱布局,仍然存在陷阱表示的潜在问题:

如果再次sizeof(Base) == sizeof(Derived),但c最终位于由Base末尾的填充覆盖的区域。将此结构体的指针传递给期望Base*并修改它的函数,可能也会影响填充位(填充具有未指定的值),从而可能破坏c,甚至创建陷阱表示。


没错。我在结构体定义之前放置了不同的 #pragma pack,并且导致了可怕的上转换结果。 - marmalmad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接