使用const成员将指向struct的指针转换为指向struct的指针

时间:2014-12-31 18:44:28

标签: c struct casting

想象一下,你有这样的结构:

struct nix_codec {
    nix_uint8 state;
    nix_uint8 mode;
    nix_uint8 flags;
    nix_size offset;
    nix_uint32 codepage;
    nix_utf8 const *const *aliases;
    void (*delete)(
        struct nix_codec *codec,
        struct nix_error *error
    );
    struct nix_codec* (*clone)(
        struct nix_codec const *codec,
        nix_int8 mode,
        struct nix_error *error
    );
    nix_size (*decode)(
        struct nix_codec *codec,
        nix_byte const *bdata,
        nix_size bsize,
        nix_rune *udata,
        nix_size usize,
        struct nix_error *error
    );
    nix_size (*encode)(
        struct nix_codec *codec,
        nix_rune const *udata,
        nix_size usize,
        nix_byte *bdata,
        nix_size bsize,
        struct nix_error *error
    );
};

typedef struct {
    nix_uint8 const state;
    nix_uint8 const mode;
    nix_uint8 const flags;
    nix_size const offset;
    nix_uint32 const codepage;
    nix_utf8 const *const *const aliases;
} nix_codec;

还有一些功能,用于创建nix_codec*实例,例如对于UTF-8编解码器,它将如下所示:

static nix_size self_decode
(
    struct nix_codec *codec,
    nix_byte const *bdata,
    nix_size bsize,
    nix_rune *udata,
    nix_size usize,
    struct nix_error *error
)
{ /* UTF-8 decode function, too long to post here */}

static nix_utf8 const *const aliases[] = {
    "UTF-8",
    "UTF8",
    "CP65001",
    NULL,
};

nix_codec *nix_codec_utf8
(
    nix_int8 mode,
    struct nix_error *error
)
{
    struct nix_codec *codec = NULL;

    if ((mode != NIX_CODEC_STRICT) && (mode != NIX_CODEC_ESCAPE)
    &&  (mode != NIX_CODEC_REPLACE) && (mode != NIX_CODEC_IGNORE)) {
        return NULL;
    }
    codec = calloc(1, sizeof(struct nix_codec));
    if (codec == NULL) {
        return NULL;
    }
    codec->mode = mode;
    codec->codepage = 65001;
    codec->aliases = aliases;
    codec->decode = &self_decode;
    codec->encode = &self_encode;
    codec->flags = (NIX_CODEC_COMPATIBLE | NIX_CODEC_MULTIBYTE | NIX_CODEC_ABSOLUTE);
    return (nix_codec*)codec;
}

传统单字节编码的功能基于以下结构:

struct nix_sbmap {
    nix_uint8 byte;
    nix_rune rune;
};

struct nix_sbcodec {
    struct nix_codec base;
    struct nix_sbmap const *entries;
    nix_size count;
};

请注意,struct nix_sbcodecstruct nix_sbmap在源文件中声明,而不是在标头中声明,因此不需要使用variant模式。相应的功能,例如nix_codec_koi8r(),分配struct nix_sbcodec,设置其baseentriescount成员,然后将其转换为nix_codec并返回。每个实际的encode()decode()调用都使用此公共函数执行:

nix_size nix_codec_decode
(
    nix_codec *codec,
    nix_byte const *bdata,
    nix_size bsize,
    nix_rune *udata,
    nix_size usize,
    struct nix_error *error
)
{
    nix_size result = 0;
    struct nix_codec *self = (struct nix_codec*)codec;

    return self->decode(self, bdata, bsize, udata, usize, error);
}

请注意statemodeflagsoffset成员可能会对使用任何编解码器的人感兴趣(其中大部分都是在编解码器创建函数中设置的,在调用offsetencode()函数后更改decode(),并表示在函数退出之前已成功处理的字节数/ Unicode字符数。每个编解码器都有自己的encode()decode()可以正常运行。

现在的问题是:这个技巧是否正确并保证可以通过C标准工作?

提前致谢!

2 个答案:

答案 0 :(得分:0)

在C中处理变体类型的可靠方法,例如我认为您正在尝试的方法,是使用union。例如:

typedef struct {
    uint8_t x;
    uint16_t y;
} obj_a;

typedef struct {
    char *p;
    char buf[42];
} obj_b;

typedef struct obj_base_s {
    int (*internal_func)(struct obj_base_s *, int);
    union {
         obj_a a;
         obj_b b;
    } u;
} obj_base;

所有创建者/析构函数都将返回或使用obj_base。使用union中的成员的函数可以直接访问其位,否则执行以下操作:

void handle_obja(obj_base *bp)
{
     obj_a *oap = &bp->u.a;
     oap->x = 23;
     oap->y = 19;
     ...
}

C C ++,如果你想要好的类,继承,重载和所有这些东西,那么使用C ++。这就是C ++发明的主要原因。 C不做“课程”。 C是一种低级语言。

答案 1 :(得分:0)

  

请注意,statemodeflagsoffset成员可能是   任何使用任何编解码器的人都很有趣(...

     

现在的问题是:这个技巧是否正确并保证能够正常工作   C标准?

它可能是有用的,但通常它可能有用,这个技巧(通过指向不同定义的struct nix_codec对象的指针访问nix_codec对象的成员)是除非你断言两种结构类型的相应成员在结构中分别具有相同的偏移量,否则不安全;对于独立定义的结构类型,这不能保证;此类保证仅存在于结构的并集中 - 请参阅语言 / 表达式 / 后缀运算符 / 结构和联合成员 /第6段:

  

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