假设我有一个矢量类:
typedef struct vec3_s
{
float x, y, z;
}
vec3;
但是,我希望能够迭代它而不将其转换为浮点数组。虽然在这种情况下演员阵容是可以接受的,但我很想知道是否有任何类似于C ++的功能在C语言中是可行的。例如,在C ++中,因为std::vector< T >
具有下标[]
运算符重载,我可以将其第一个索引的地址传递给一个void*
的函数。
即,
void do_something_with_pointer_to_mem( void* mem )
{
// do stuff
}
int main( void )
{
std::vector< float > v;
// fill v with values here
// pass to do_something_with_pointer_to_mem
do_some_with_pointer_to_mem( &v[ 0 ] );
return;
}
另一个更具体的例子是在OpenGL中使用glBufferData(...)时(使用C ++时):
glBufferData( GL_ARRAY_BUFFER, sizeof( somevector ), &somevector[ 0 ], GL_STREAM_DRAW );
那么,是否可以使用下标运算符在C中完成类似的操作?如果没有,并且我必须编写一个函数(例如float vec3_value_at( unsigned int i )
),那么在它定义的头文件中只有static inline
它是否有意义?
答案 0 :(得分:11)
如果您的所有结构字段属于同一类型,则可以使用以下联合:
typedef union vec3_u
{
struct vec3_s {
float x, y, z;
};
float vect3_a[3];
}
vec3;
这样,您可以独立访问每个x,y或z字段,或使用vect3_a数组迭代它们。这个解决方案在内存或计算方面没有任何成本,但我们可能与C ++类似的解决方案有点远。
答案 1 :(得分:2)
你没有得到C ++的语法糖,但很容易用C ++编写的函数作为operator []。
float get_vec3(v *vec3, int i) {
switch(i) {
case 0: return v->x;
case 1: return v->y;
case 2: return v->z;
}
assert(0);
}
现在你可以遍历任何vec3。
for (int i = 0; i < 3; i++) {
printf("%f\n", get_vec3(v, i));
}
答案 2 :(得分:1)
C中的问题与您要做的是您需要知道如何在结构中移动(即您需要知道类型)。 std::vector<T>
的工作原理是因为它使用模板(C++
概念)。现在,说,你可以尝试一些与你建议的略有不同的东西。如果您不想使用任何数组,则可以存储泛型类型。但是,在检索数据并使用它时,用户必须知道他或她期望的数据类型。下面避免了数组(虽然,使用它们时可能存在一个更清晰的解决方案)并且有一个链接列表实现,它给你几乎相同的std::vector<T>
灵活性(除了性能优势,因为这是一个{{1的链表)对所有事情的操作(你可以聪明地反转列表来实现,也许是O(n)
插入,但这仅仅是为了举例)
O(1)
此代码应该开箱即用。你在这里有一个链表,它是你的“矢量”(特别是vec3结构)。列表中的每个节点(即#include <stdio.h>
#include <stdlib.h>
typedef struct _item3_t
{
void *x, *y, *z;
struct _item3_t* next;
} item3_t;
typedef struct
{
item3_t* head;
} vec3_t;
void insert_vec3(vec3_t* vec, void* x, void* y, void* z)
{
item3_t* item = NULL;
item3_t* tmp = NULL;
int i = 0;
if(vec == NULL)
return;
item = malloc(sizeof(item3_t));
item->x = x;
item->y = y;
item->z = z;
item->next = NULL;
tmp = vec->head;
if(tmp == NULL) { // First element
vec->head = item;
} else {
while(tmp->next != NULL)
tmp = item->next;
tmp->next = item;
}
}
// This is one method which simply relies on the generic method above
void insert_vec3_float(vec3_t* vec, float x, float y, float z)
{
float* xv, *yv, *zv;
if(vec == NULL)
return;
xv = malloc(sizeof(float));
yv = malloc(sizeof(float));
zv = malloc(sizeof(float));
*xv = x;
*yv = y;
*zv = z;
insert_vec3(vec, xv, yv, zv);
}
void init_vec3(vec3_t* vec)
{
if(vec == NULL)
return;
vec->head = NULL;
}
void destroy_vec3(vec3_t* vec)
{
item3_t* item = NULL, *next = NULL;
if(vec == NULL)
return;
item = vec->head;
while(item != NULL) {
next = item->next;
free(item->x);
free(item->y);
free(item->z);
free(item);
item = next;
}
}
item3_t* vec3_get(vec3_t* vec, int idx)
{
int i = 0;
item3_t* item = NULL;
if(vec == NULL)
return NULL;
item = vec->head;
for(i = 0 ; i < idx && item != NULL ; ++i)
item = item->next;
return item;
}
void do_something(item3_t* item)
{
if(item == NULL)
return;
float x = *((float*)item->x);
float y = *((float*)item->y);
float z = *((float*)item->z);
// To do - something? Note, to manipulate the actual
// values in the vector, you need to modify their values
// at their mem addresses
}
int main()
{
vec3_t vector;
init_vec3(&vector);
insert_vec3_float(&vector, 1.2, 2.3, 3.4);
printf("%f %f %f\n", *((float*)vec3_get(&vector, 0)->x), *((float*)vec3_get(&vector, 0)->y), *((float*)vec3_get(&vector, 0)->z));
do_something(vec3_get(&vector, 0));
destroy_vec3(&vector);
return 0;
}
意义上的每个元素)都有3个元素,它们都是std::vector<T>
个指针。因此,您可以存储您希望的任何数据类型。唯一的问题是你需要为那些指向指针的指针分配内存,当你删除一个元素时,你需要释放那个内存(例如,请参考void
方法)。希望这有助于理解这些void指针在您的情况下如何工作。
要检索数据,您将无法使用vec3_destroy
表示法,但可以采用相同的方式使用[]
方法。 vec3_get
方法是一种示例存根,您可以通过某种方式完成类似于您在OP中提到的内容。