16

plain C 有很好的特性 - void 类型指针,它可以用作指向任何数据类型的指针。
但是,假设我有以下结构:


struct token {
    int type;
    void *value;
};

其中 value 字段可能指向 char 数组、int 或其他内容。
所以在分配这个结构的新实例时,我需要:

1) 为这个结构分配内存;
2)为值分配内存并将其分配给值字段。

我的问题是 - 有没有办法声明“类型为 void 的数组”,它可以转换为任何其他类型,如 void 指针?

我想要的只是使用具有转换为任何类型的能力的“灵活成员数组”(在 C99 标准的 6.7.2.1 中描述)。

像这样的东西:


struct token {
    int type;
    void value[];
};

struct token *p = malloc(sizeof(struct token) + value_size);
memcpy(p->value, val, value_size);
...
char *ptr = token->value;

我想将 token->value 声明为 char 或 int 数组并稍后转换为所需的类型将完成这项工作,但对于稍后将阅读此代码的人来说可能会非常混乱。

4

6 回答 6

5

嗯,有点,但它可能不是你想要的:

struct token {
  // your fields
  size_t item_size;
  size_t length
};

struct token *make_token(/* your arguments */, size_t item_size, size_t length)
{
    struct token *t = malloc(sizeof *t + item_size * length);
    if(t == NULL) return NULL;
    t->item_size = item_size;
    t->length    = length;
    // rest of initialization
}

以下宏可用于索引您的数据(假设x是 a struct token *):

#define idx(x, i, t) *(t *)(i < x->length ? sizeof(t) == x->item_size ?
                       (void *)(((char *)x[1]) + x->item_size * i)
                     : NULL : NULL)

而且,如果您愿意,以下宏可以包装您的make_token函数,使其更直观(或者更骇人听闻,如果您这样想的话):

#define make_token(/* args */, t, l) (make_token)(/* args */, sizeof(t), l)

用法:

struct token *p = make_token(/* args */, int, 5); // allocates space for 5 ints
...
idx(p, 2, int) = 10;
于 2011-04-25T22:20:14.150 回答
2

扩展 AShelly 的答案,您可以这样做;

/** A buffer structure containing count entries of the given size. */
typedef struct {
    size_t size;
    int count;
    void *buf;
} buffer_t;

/** Allocate a new buffer_t with "count" entries of "size" size. */
buffer_t *buffer_new(size_t size, int count)
{
    buffer_t *p = malloc(offsetof(buffer_t, buf) + count*size);
    if (p) {
        p->size = size;
        p->count = count;
    }
    return p;
}

注意在分配内存时使用“offsetof()”而不是“sizeof()”以避免浪费“void *buf;” 场大小。“buf”的类型无关紧要,但使用“void *”意味着它将针对指针优化对齐结构中的“buf”字段,并在需要时在其前面添加填充。这通常为条目提供更好的内存对齐,特别是如果它们至少与指针一样大。

访问缓冲区中的条目如下所示;

/** Get a pointer to the i'th entry. */
void *buffer_get(buffer_t *t, int i)
{
    return &t->buf + i * t->size;
}

请注意额外的地址操作符,以获取“buf”字段的地址作为分配的条目内存的起点。

于 2017-06-26T05:05:51.270 回答
1

我可能会这样做:

struct token {
    int type;
    void *value;
};

struct token p;

p.value = malloc(value_size);

p.value[0] = something;
p.value[1] = something;
...

编辑,实际上你必须对那些 p.value[index] = somethings 进行类型转换。和/或使用联合不必进行类型转换。

于 2011-04-25T22:09:48.083 回答
1

你不能有一个“无效”项目的数组,但你应该能够做你想做的事情,只要你在做 malloc 时知道 value_size。但这不会很漂亮。

struct token {
        int type;
        void *value;       
};    

value_size = sizeof(type)*item_count;
struct token *p = malloc(sizeof(struct token) + value_size);
//can't do memcpy:  memcpy(p->value, val, value_size);  
//do this instead
type* p = (type*)&(p->value);
type* end = p+item_count;
while (p<end) { *p++ = someItem; }

请注意,当您想要获得额外的存储空间时,您需要一个额外的地址运算符。

type *ptr = (type*)&(token->value); 

这将“浪费” sizeof(void*) 字节,并且原始类型value并不重要,因此您不妨使用较小的项目。我可能会typedef char placeholder;value那种类型。

于 2011-04-25T22:22:12.390 回答
0

以下结构可以帮助您。

struct clib_object_t {
void* raw_data;
size_t size;
};

struct clib_object_t*
new_clib_object(void *inObject, size_t obj_size) {
    struct clib_object_t* tmp = (struct clib_object_t*)malloc(sizeof(struct clib_object_t));   
    if ( ! tmp )
        return (struct clib_object_t*)0;
    tmp->size        = obj_size;
    tmp->raw_data    = (void*)malloc(obj_size);
    if ( !tmp->raw_data ) {
        free ( tmp );
        return (struct clib_object_t*)0;
    }
    memcpy ( tmp->raw_data, inObject, obj_size);
    return tmp;
}

clib_error
get_raw_clib_object ( struct clib_object_t *inObject, void**elem) {
    *elem = (void*)malloc(inObject->size);
    if ( ! *elem )
        return CLIB_ELEMENT_RETURN_ERROR;
    memcpy ( *elem, inObject->raw_data, inObject->size );

    return CLIB_ERROR_SUCCESS;
}

更多细节:clibutils

于 2011-04-26T07:55:21.640 回答
0

c/c++中不支持void 类型的数组。像这样的例子:

int main() {

 void  alexa[]; // error: declaration of ‘alexa’ as array of void 

return 0;

}

c/c++ 支持void 指针数组。下面的例子:

int main(int argc, char argv*[]) 
{

void *alexa[100]; // Compiled successfully

return 0;

}
于 2021-03-12T17:40:47.953 回答