C 通过已知的 sizeof 迭代数组

C iterate array by known sizeof

我正在尝试制作一个用于粒子管理的小型库,允许使用用户数据(纹理、动画帧等)“扩展”结构。该库将只知道扩展结构的大小。 如何遍历未知结构类型但已知结构大小的数组?

typedef struct{
    int type;
}Base;

typedef struct{
    Base base;
    int value;
}inherited;

int main(){
    size_t size = sizeof(inherited);
    int count = 10;
    
    void *b = malloc(size * count);
    
    for (int i = 0; i < count; i++){
        // iterate based on known size & send to callback
        callback( &( (size)b )[i] );
    }
    
    free(b);
    return 0;
}
for( inherited *p = b, *e = p + count; p < e; p++ ){
    callback(p);
}

我假设执行 malloc 和调用 callback 的代码不知道对象的类型,只知道它的大小。

#include <stdlib.h>

void *alloc_and_init(size_t nmemb, size_t size, void (*callback)(void *))
{
    void *b = calloc(nmemb, size);
    if (b)
    {
        char *p = b;
        for (size_t i = 0; i < nmemb; i++)
        {
            callback(p);
            p += size;
        }
    }
    return b;
}

typedef struct{
    int type;
}Base;

typedef struct{
    Base base;
    int value;
}inherited;

void init_inherited(void *p)
{
    inherited *obj = p;
    /* do initialization of obj->* here */
}

int main(void)
{
    int objcount = 10;
    inherited *objarr;

    objarr = alloc_and_init(objcount, sizeof(*objarr),
                            init_inherited);
    /* ... */
    free(objarr);
}

C 中的多态性总是相当笨拙。基本上你必须手动构建一个“vtable”。下面的朴素、简化版本让每个对象都有自己的函数指针。你最终会得到像这样做作的东西:

#include <stdio.h>
#include <stdlib.h>

typedef struct base_t base_t;
typedef void callback_t (const base_t* arg);

struct base_t
{
  int type;
  callback_t* callback;
};

typedef struct
{
  base_t base;
  int value;
} inherited_t;


void callback_base (const base_t* arg)
{
  puts(__func__);
}

void callback_inherited (const base_t* arg)
{
  const inherited_t* iarg = (const inherited_t*)arg;
  printf("%s value: %d\n", __func__, iarg->value);
  
}

int main (void)
{
  // allocate memory
  base_t* array [3] = 
  {
    [0] = malloc(sizeof(inherited_t)),
    [1] = malloc(sizeof(base_t)),
    [2] = malloc(sizeof(inherited_t)),
  };

  // initialize objects
  *(inherited_t*)array[0] = (inherited_t){ .base.callback=callback_inherited, .value = 123 };
  *(array[1]) = (base_t){ .callback=callback_base };
  *(inherited_t*)array[2] = (inherited_t){ .base.callback=callback_inherited, .value = 456 };
  
  for (int i = 0; i < 3; i++)
  {
    array[i]->callback(array[i]); // now we get polymorphism here
  }
}

更专业的版本涉及为每个“class”编写一个翻译单元(.h + .c),然后在“构造函数”中将分配与初始化结合起来。它将使用不透明类型实现,参见 How to do private encapsulation in C? 在构造函数中,设置与分配的对象类型对应的 vtable。

我还大胆声称任何使用 void* 参数的 OO 解决方案都存在一些设计缺陷。该接口应该使用基 class 指针。空指针很危险。

char *b = malloc(size * count);

for (int i = 0; i < count; i++){
    // iterate based on known size & send to callback
    callback( b + i * size );
}