从 C 中的缓冲区分配



>我正在构建一个简单的粒子系统,并希望使用单个结构数组缓冲区来管理我的粒子。 也就是说,我找不到允许我从任意缓冲区使用 malloc() 和 free() 的 C 函数。 这里有一些伪代码来显示我的意图:

Particle* particles = (Particle*) malloc( sizeof(Particle) * numParticles );
Particle* firstParticle = <buffer_alloc>( particles );
initialize_particle( firstParticle );
// ... Some more stuff
if (firstParticle->life < 0)
    <buffer_free>( firstParticle );
// @ program's end
free(particles);

其中<buffer_alloc><buffer_free>是从任意指针(可能具有缓冲区长度等附加元数据)分配和释放内存块的函数。 是否存在此类功能和/或是否有更好的方法? 谢谢!

是的,你必须自己写。 它是如此简单,真的很愚蠢,但与一直简单地使用 malloc() 和 free() 相比,它的性能会尖叫......

static const int maxParticles = 1000;
static Particle particleBuf[maxParticles]; // global static array
static Particle* headParticle;
void initParticleAllocator()
{
    Particle* p = particleBuf;
    Particle* pEnd = &particleBuf[maxParticles-1];
    // create a linked list of unallocated Particles
    while (p!=pEnd)
    {
        *((Particle**)p) = p+1;
        ++p;
    }
    *((Particle**)p) = NULL; // terminate the end of the list
    headParticle = particleBuf; // point 'head' at the 1st unalloc'ed one
}
Particle* ParticleAlloc()
{
    // grab the next unalloc'ed Particle from the list
    Particle* ret = headParticle;
    if (ret)
        headParticle = *(Particle**)ret;
    return ret; // will return NULL if no more available
}
void ParticleFree(Particle* p)
{
    // return p to the list of unalloc'ed Particles
    *((Particle**)p) = headParticle;
    headParticle = p;
}

你可以修改上面的方法,根本不从任何全局静态数组开始,并在用户调用 ParticleAlloc() 时首先使用 malloc(),但是当返回粒子时,不要调用 free(),而是将返回的粒子添加到未分配粒子的链列表中。 然后下一个调用 ParticleAlloc() 的人将从自由粒子列表中获取一个,而不是使用 malloc()。 每当空闲列表中没有更多内容时,您的 ParticleAlloc() 函数就可以回退到 malloc()。 或者混合使用这两种策略,这确实是两全其美的:如果你知道你的用户几乎肯定会使用至少 1000 个粒子,但偶尔可能需要更多,你可以从 1000 个静态数组开始,如果你用完了,就回退调用 malloc()。 如果你这样做,malloc()'ed 的不需要特殊处理;只需将它们添加到未分配粒子列表中,当它们返回到 ParticleFree() 时即可。 当你的程序退出时,你不需要费心调用它们 free();操作系统将释放进程的整个内存空间,因此任何泄漏的内存都将在此时清除。

我应该提到,由于您的问题被标记为"C"而不是"C++",因此我以 C 解决方案的形式回答了它。 在C++中,实现相同内容的最佳方法是将"运算符新"和"运算符删除"方法添加到 Particle 类中。 它们将包含与我上面显示的基本相同的代码,但它们覆盖(而不是重载)全局"new"运算符,并且仅针对 Particle 类定义一个替换全局"new"的专用分配器。 很酷的是,粒子对象的用户甚至不必知道有一个特殊的分配器;他们只是像往常一样使用"新建"和"删除",并且仍然幸福地不知道他们的粒子对象来自一个特殊的预分配池。

,对不起。这个问题是C只有我看到。不C++。好吧,如果是C++以下内容将为您提供帮助。

查看 Boost 的池分配库。

在我看来,您的每个分配大小相同?粒子的大小,对吗?如果是这样,Boost 中的池分配函数将非常有效,您不必编写自己的池分配函数。

你必须自己写,或者找一个已经写过的人并重用他们写的东西。 没有一个标准的 C 库来管理这种情况,AFAIK。

对于"缓冲区分配"代码,您可能需要 4 个函数:

typedef struct ba_handle ba_handle;
ba_handle *ba_create(size_t element_size, size_t initial_space);
void  ba_destroy(ba_handle *ba);
void *ba_alloc(ba_handle *ba);
void  ba_free(ba_handle *ba, void *space);

创建函数将进行空间的初始分配,并安排以element_size为单位划分信息。 返回的句柄允许您为不同类型的(甚至多次为同一类型)分配单独的缓冲区。 销毁函数强制释放与句柄关联的所有空间。

分配函数为您提供了新的空间单位以供使用。 免费函数发布它以供重用。

在后台,代码跟踪正在使用的单位(也许是位图),并可能根据需要分配额外的空间,或者在初始分配用完时拒绝空间。 您可以安排它在空间不足时或多或少地失败(因此分配器永远不会返回空指针)。 显然,free 函数可以验证它给出的指针是否由当前正在使用的缓冲区分配器句柄提供。 这允许它检测一些常规free()通常无法检测到的错误(尽管 malloc() 等人的 GNU C 库版本似乎确实做了一些其他人不一定做的健全性检查)。

也许可以尝试这样的事情...

Particle * particles[numParticles];
particles[0] = malloc(sizeof(Particle));
initialize_particle( particle[0] );
// ... Some more stuff
if (particle[0]->life < 0)
    free( particle[0] );
// @ program's end
// don't free(particles);

我正在构建一个简单的粒子系统,并希望使用单个结构数组缓冲区来管理我的粒子。

我想你回答了:

static Particle myParticleArray[numParticles];

在程序开始时分配,在程序结束时取消分配,很简单。 或者像你的伪代码一样,一次对数组进行malloc处理。 你可能会问自己,为什么要分配一个粒子,为什么不分配整个系统? 编写 API 函数以获取指向粒子数组和索引的指针。

最新更新