用于迭代像数组一样的struct成员的C方法?

时间:2013-01-19 20:35:31

标签: c vector iteration memory-address subscript

假设我有一个矢量类:

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它是否有意义?

3 个答案:

答案 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中提到的内容。