2011-04-25 40 views
6

plain C có các con trỏ kiểu void - feature void, có thể được sử dụng như con trỏ tới bất kỳ kiểu dữ liệu nào.
Nhưng, giả sử tôi có sau struct:mảng loại void


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

nơi trường giá trị có thể trỏ đến char mảng, hoặc int, hay cái gì khác.
Vì vậy, khi phân bổ trường hợp mới của cấu trúc này, tôi cần:

1) cấp phát bộ nhớ cho cấu trúc này;
2) cấp phát bộ nhớ cho giá trị và gán cho trường giá trị.

Câu hỏi của tôi là - có cách nào để khai báo "mảng loại void", có thể được truyền sang bất kỳ loại nào khác như con trỏ trống không?

Tất cả những gì tôi muốn là sử dụng "mảng thành viên linh hoạt" (được mô tả trong 6.7.2.1 của tiêu chuẩn C99) có khả năng truyền sang bất kỳ loại nào.

Something như thế này:


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; 

Tôi cho rằng tuyên bố token-> giá trị như char hoặc mảng int và đúc để loại cần thiết sau này sẽ làm công việc này, nhưng có thể rất khó hiểu đối với một ai đó sẽ đọc mã sau.

+0

Con trỏ kép? –

+2

sử dụng 'char []' là tốt, vì 'sizeof (char) == 1' và bạn sẽ không bao giờ ngạc nhiên. Bạn có thể muốn xem xét các macro để truy cập 'p-> giá trị' với loại chính xác. –

Trả lời

1

tôi có lẽ sẽ làm điều này:

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

struct token p; 

p.value = malloc(value_size); 

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

chỉnh sửa, thực sự bạn phải định kiểu những p.value [index] = somethings. Và/hoặc sử dụng một liên minh để không phải đánh máy.

+0

Bạn sẽ phải truyền 'p.value' trước khi sử dụng. –

+0

'p.value [0] = something' sẽ tạo ra một lỗi biên dịch thời gian, chắc chắn? Người ta không thể dereference một 'void *'. –

+0

nụ cười, bạn đánh tôi với nó ... đã cố gắng để có được chỉnh sửa một cách nhanh chóng. –

3

Vâng, loại, nhưng có lẽ không phải là điều bạn muốn:

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 
} 

vĩ mô sau đây có thể được sử dụng để chỉ mục dữ liệu của bạn (giả sử x là một 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) 

Và , nếu bạn thích, macro sau có thể bọc chức năng make_token của bạn để làm cho nó trực quan hơn một chút (hoặc nhiều hơn, nếu bạn nghĩ về nó theo cách đó):

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

Cách sử dụng:

struct token *p = make_token(/* args */, int, 5); // allocates space for 5 ints 
... 
idx(p, 2, int) = 10; 
1

Bạn không thể có một loạt các mặt hàng 'khoảng trống', nhưng bạn sẽ có thể làm điều gì đó giống như những gì bạn muốn, miễn là bạn biết value_size khi bạn làm malloc. Nhưng nó sẽ không đẹp.

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; } 

Lưu ý rằng bạn cần thêm một toán tử địa chỉ khi bạn muốn nhận thêm bộ nhớ.

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

này sẽ 'lãng phí' sizeof byte (* void), và các loại ban đầu của value không thực sự quan trọng, vì vậy bạn cũng có thể sử dụng một mục nhỏ hơn. Tôi có thể là typedef char placeholder; và tạo value loại đó.

0

cấu trúc sau có thể giúp bạn.

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; 
} 

Xem chi tiết: clibutils

0

Mở rộng về câu trả lời AShelly của bạn có thể làm điều này;

/** 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; 
} 

Lưu ý việc sử dụng "offsetof()" thay vì "sizeof()" khi cấp phát bộ nhớ để tránh lãng phí "void * buf;" kích thước trường. Loại "buf" không quan trọng lắm, nhưng sử dụng "void *" có nghĩa là nó sẽ căn chỉnh trường "buf" trong cấu trúc tối ưu cho một con trỏ, thêm phần đệm vào trước nếu cần. Điều này thường cho phép liên kết bộ nhớ tốt hơn cho các mục, đặc biệt nếu chúng ít nhất là lớn như một con trỏ.

Truy cập các mục trong bộ đệm trông như thế này;

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

Lưu ý địa chỉ bổ sung của toán tử để lấy địa chỉ của trường "buf" làm điểm bắt đầu cho bộ nhớ mục được cấp phát.

Các vấn đề liên quan