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
以下宏可用于索引您的数据(假设x
是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;
【讨论】:
【参考方案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指针从任何类型访问字节数组