C结构的可变成员列表,这可能吗?

时间:2011-04-07 04:20:15

标签: c interface struct variadic-functions

我有一个关于具有“变量成员列表”的结构的问题,类似于“变量参数列表”,我们可以将函数定义为具有。 就C语言基础而言,我可能听起来很愚蠢或完全不合适,但如果我错了,请纠正我。

那么我可以有这样的C结构:

 
    struct Var_Members_Interface
    {
        int intMember;
        char *charMember;
        ... // is this possible?
    };

我的想法是拥有一个可以由类实现的c样式接口,但这些类在此结构中可以有其他成员。但是,它们必须具有intMember和charMember。

提前致谢。

6 个答案:

答案 0 :(得分:2)

C99中最接近的近似值(但不是C89)是在结构末端有一个灵活的阵列成员:

struct Var_Members_Interface
{
    int   intMember;
    char *charMember;
    Type  flexArrayMember[];
};

现在,您可以在结尾处使用类型为Type的数组动态分配结构,并访问该数组:

struct Var_Members_Interface *vmi = malloc(sizeof(*vmi) + N * sizeof(Type));

vmi->flexArrayMember[i] = ...;

请注意,这不能在C ++中使用。

但这与你所追求的并不是非常接近。你所追求的是不能用单一结构类型在C中完成,并且只能通过继承在C ++中近似 - 参见其他答案。


你可以逃脱的一个技巧 - 通常 - 在C中使用多种结构类型和大量的强制转换:

struct VM_Base
{
    int   intMember;
    char *charMember;
};

struct VM_Variant1
{
    int   intMember;
    char *charMember;
    int   intArray[3];
};

struct VM_Variant2
{
    int   intMember;
    char *charMember;
    Type  typeMember;
};

struct VM_Variant3
{
    int     intMember;
    char   *charMember;
    double  doubleMember;
};

现在,通过一些大锤演员,您可以编写带有“struct VM_Base *”参数的函数,并传入指向任何VM_VariantN类型的指针。 'intMember'可能用于说明您实际拥有哪些变体。这或多或少与POSIX套接字函数有关。存在不同类型的套接字地址,并且结构具有不同的长度,但是它们具有共同的前缀,并且最终被调用正确的代码,因为公共前缀标识套接字地址的类型。 (设计并不优雅;但它是标准的 - BSD套接字的事实标准 - 在POSIX标准化之前。而BSD设计在C89之前,更不用说C99了。现在是从头开始设计,没有要求为了与现有代码兼容,可以采用不同的方式。)

这种技术像罪一样丑陋,需要强大的演员才能使它编译 - 并且非常注意使其正常工作。你不应该为C ++中的这种混乱而烦恼。

答案 1 :(得分:1)

在C语言中使用直接语言支持不能做这样的事情;但是在C ++中,扩展结构的类将继承这些数据成员并可以添加自己的数据。所以在C ++中,不仅可以你这样做,而且这是一种正常的操作模式。

答案 2 :(得分:1)

首先,您需要了解struct到底是什么。

C中的结构只是用于解释内存中的标准。

要了解这意味着什么,让我们使用你的结构:

struct Var_Members_Interface
{
    int intMember;
    char *charMember;
};

struct Var_Members_Interface instance; //An instance of the struct

这意味着,“我将保留一些内存并将其称为instance,我会将前几个字节解释为一个整数,而接下来的几个字节意味着指向某个地方在记忆中。“

鉴于此,拥有“变量成员”结构是没有意义的,因为结构只是现有内存块的布局规范 - 和现有的块没有“可变”的长度。

答案 3 :(得分:1)

你可以像旧的X11 Xt widget library那样做:

struct Var_Members_Interface {
    int intMember;
    char *charMember;
};
struct Other_Part {
    int extraInt;
    char *extraString;
}
struct Var_Other_Interface {
    struct Var_Members_Interface base;
    struct Other_Part other;
};

只要您对分配,对齐和填充问题小心,那么这将有效:

struct Var_Other_Interface   *other      = create_other();
struct Var_Members_Interface *member     = (struct Var_Other_Interface *)other;
struct Var_Other_Interface   *back_again = (struct Var_Other_Interface)member;

您可以根据需要深入嵌套结构以获得单个继承层次结构。

这种事情不适合佯攻:你必须非常小心你的分配,结构嵌套等。

看看旧学校的Xt小部件,你会明白的; Xt小部件通常在三个文件中实现:C源文件,带有功能接口的公共头,以及用于定义结构布局的私有头(子类化需要这个)。

例如,我以前在mgv中使用的Ghostscript小部件如下所示:

typedef struct {
    /* Bunch of stuff. */
} GhostviewPart;

typedef struct _GhostviewRec {
        CorePart      core;
        GhostviewPart ghostview;
} GhostviewRec;

CorePart是标准的Xt小部件定义,GhostviewRec是实际的小部件本身。

答案 4 :(得分:0)

不完全是您要查找的内容,但您可以在结构中创建一个void指针,该指针可用于指向定义新类型的另一个结构。

struct Var_Members_Interface
    {
        int intMember;
        char *charMember;
        void *otherMembers;
    };

修改:此article中的解决方案可能更接近您所寻找的内容。

答案 5 :(得分:0)

我认为你有两个选择。你可以模仿C ++的功能但不幸的是,你必须看到所有的血腥细节。您可以定义公共基础结构,并将其作为所有变体结构的成员,例如

struct VM_Base
{
    int   intMember;
    char *charMember;
};

struct VM_Variant1
{
    struct VM_Base base;
    int   foo;
};

struct VM_Variant2
{
    struct VM_Base base;
    char *charMember;
    double bar;
};

struct VM_Variant3
{
    struct VM_Base base;
    char   *charMember;
    char baz[10];
};

指向任何变体结构的指针也是指向变体结构的基本成员的指针,因此您可以自由地转换为基本成员。回到另一个方向显然更成问题,因为你需要检查以确保你投射到正确的类型。

你可以使用union来取消强制转换,例如

struct VM_Variant1
{
    struct VM_Base base;
    int   foo;
};

struct VM_Variant2
{
    struct VM_Base base;
    char *charMember;
    double bar;
};

struct VM_Variant3
{
    struct VM_Base base;
    char   *charMember;
    char baz[10];
};

struct VM
{
    int   intMember;
    char *charMember;
    union
    {
        struct VM_Variant1 vm1;
        struct VM_Variant2 vm2;
        struct VM_Variant3 vm3;
    }
};

第二种方法不需要类型转换。您可以像这样访问成员:

double aDouble = aVMStruct.vm2.bar;

联合的三个成员在内存中相互叠加,因此分配的块只是三个变体中最大的一个的大小。