【问题标题】:C iterate array by known sizeofC通过已知sizeof迭代数组
【发布时间】:2021-11-17 17:12:13
【问题描述】:

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

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

【问题讨论】:

  • “我如何遍历未知结构类型但结构大小已知的数组?” - 如果您不知道类型,那么您甚至都不知道知道尺码吧?要么你都知道,要么你都不知道。
  • 为什么不使用指向inherited 的指针? inherited *p = malloc(size * count); 使用此指针,您可以使用 callback(&amp;p[i]); 寻址每个数组项。
  • @SPlatten 您的建议只有在将 b 声明从 void* 更改为 char* 后才会起作用。 void 类型没有大小,所以 void* 不能用于指针算术。
  • 相关:typedef struct{ int type; size_t size; }Base; 可能不是个坏主意?
  • @NeZvers 我的意思是类似于 Ian Abbott 的回答。

标签: arrays c pointers sizeof


【解决方案1】:
for( inherited *p = b, *e = p + count; p < e; p++ ){
    callback(p);
}

【讨论】:

  • 继承类型未知,只有base struct和sizeof(Inherited)。 Ian 给出了一个完美的解决方案,将其转换为 char 并通过 (i * size) 进行迭代。
【解决方案2】:

我假设执行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);
}

【讨论】:

  • 是的,完美的答案。我需要唯一的交互解决方案,但解决方案响亮而清晰。谢谢!
【解决方案3】:

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

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

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

【讨论】:

  • 感谢您的版本,但它太过分了。 Ian Abbott 发布了完美的解决方案。我只需要一个 char 指针并使用 (i * size) 进行迭代。
【解决方案4】:
char *b = malloc(size * count);

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

【讨论】:

  • 谢谢!这是我需要的最正确、最简洁的答案。
  • 如果不是使用 char* 和 size,而是创建一个与 'size' 大小相同的结构,然后可以使用它,效果会更好。
  • @SPlatten 不会的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 2011-01-13
  • 2015-09-25
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 2016-10-24
相关资源
最近更新 更多