プレーンCにはNice機能があります-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配列として宣言し、後で必要な型にキャストすることでこの作業ができると思いますが、後でこのコードを読む人にとっては非常に混乱する可能性があります。
まあ、ある種ですが、それはおそらくあなたが望むものではありません:
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;
私はおそらくこれを行うでしょう:
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をタイプキャストする必要があります。および/またはタイプキャストする必要がないようにユニオンを使用します。
'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; }
追加のストレージを取得する場合は、追加のaddress-of演算子が必要であることに注意してください。
type *ptr = (type*)&(token->value);
これにより、sizeof(void *)バイトが「無駄」になり、元のタイプのvalue
は実際には重要ではないため、小さいアイテムを使用することもできます。私はおそらくtypedef char placeholder;
そしてvalue
をそのタイプにします。
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;
}
「void * buf;」の無駄を避けるために、メモリを割り当てるときに「sizeof()」の代わりに「offsetof()」を使用することに注意してください。フィールドサイズ。 「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」フィールドのアドレスを取得するための追加のaddress-of演算子に注意してください。
次の構造が役立ちます。
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