void 类型的数组

Posted

技术标签:

【中文标题】void 类型的数组【英文标题】:array of type void 【发布时间】:2011-08-12 15:07:15 【问题描述】:

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


struct token 
    int type;
    void *value;
;

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

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

我的问题是 - 有没有办法声明“array of type 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 数组并稍后转换为所需的类型将完成这项工作,但对于稍后将阅读此代码的人来说可能会非常混乱。

【问题讨论】:

使用char[] 很好,因为sizeof(char) == 1 你永远不会感到惊讶。您可能需要考虑使用正确类型访问 p->value 的宏。 【参考方案1】:

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

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

以下宏可用于索引您的数据(假设xstruct 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;

【讨论】:

【参考方案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;

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

【讨论】:

这个语句中的一个小初学者问题:buffer_t *p = malloc(offsetof(buffer_t, buf) + count*size); 你不需要添加一个额外的count * sizeof(int) 来解释结构中的count 字段吗?还是您假设size 字段已经包含int 的大小? 在我的示例中,“size”字段是每个条目的字节大小,count 是分配的条目数,因此每个条目数组只有一个计数。【参考方案3】:

我可能会这样做:

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 进行类型转换。和/或使用联合不必进行类型转换。

【讨论】:

在使用它之前,您必须先投射 p.value p.value[0] = something 肯定会产生编译时错误吗?不能取消引用 void* 这甚至不会编译。一些编译器允许对 void * 指针(作为扩展)进行指针运算,但它们都禁止取消引用 void 指针。【参考方案4】:

你不能有一个'void'项目的数组,但你应该能够做你想做的事情,只要你在做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 成为那种类型。

【讨论】:

【参考方案5】:

以下结构可以帮助您。

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

【讨论】:

【参考方案6】:

type void 数组在 c/c++ 中支持。 例如:

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;


【讨论】:

以上是关于void 类型的数组的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 允许通过C中的void指针从任何类型访问字节数组

Java 用数组类型输出菱形??

1.动态数组

c# 调用c dll void 指针类型转化问题

李连杰的二级指针,二级指针的用途,多级指针的定义使用,数组与指针的区别,void 类型指针,引用

从函数中的 void * 参数返回数组