2012-06-24 2 views
2

Я создаю простую систему частиц и хочу использовать один буфер массива структур для управления моими частицами. Тем не менее, я не могу найти функцию 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> являются функции, которые выделяют и свободные куски памяти от произвольных указателей (возможно, с дополнительными метаданными, такие как длина буфера, и т.д.). Существуют ли такие функции и/или есть ли лучший способ сделать это? Спасибо!

+0

Не могли бы вы добавить определение для (struct?) Particle? Он содержит указатели? – wildplasser

+1

+1. Насколько я знаю, C не делает то, что вы хотите. C++ делает это, используя синтаксис * места размещения *, который является забавным. Тем не менее, ничто не мешает вам выделять массив частиц (будь то в стеке или в куче), а затем по мере необходимости распределять хранилище для одной частицы. – thb

+0

@thb: Я не думаю, что это новый сценарий размещения. OP хочет автоматическое управление памятью, но изолирован от определенной области памяти. –

ответ

5

Да, вы должны написать свой собственный. Это так просто, что это действительно глупо, но его производительность будет кричать по сравнению с просто используя таНос() и свободный() все время ....

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

Вы можете изменить подход выше не начать с какой-либо глобальной статический массив вообще, и сначала используйте malloc(), когда пользователь вызывает ParticleAlloc(), но когда возвращается Particles, не вызывайте free(), а вместо этого добавляйте возвращенные в связанный список неаллокуемых частиц. Затем следующий вызывающий элемент ParticleAlloc() получит один из списка свободных частиц, а не использует malloc(). Каждый раз, когда в свободном списке больше нет, ваша функция ParticleAlloc() может затем вернуться к malloc(). Или используйте сочетание двух стратегий, которые действительно были бы лучшими из обоих миров: если вы знаете, что ваш пользователь почти наверняка будет использовать по крайней мере 1000 частиц, но иногда может потребоваться больше, вы можете начать с статического массива 1000 и вернитесь на вызов malloc(), если закончите. Если вы это сделаете, для malloc() 'ed не требуется специальная обработка; просто добавьте их в свой список неаллектированных частиц, когда они вернутся к ParticleFree(). Вам не нужно беспокоиться о вызове free() на них, когда ваша программа завершается; операционная система освободит весь объем памяти процесса, так что всякая просочившаяся память исчезнет в этот момент.

Следует отметить, что, поскольку вы задали вопрос «C», а не «C++», я ответил на него в форме решения C. В C++ лучшим способом реализовать эту же задачу было бы добавить методы «operator new» и «operator delete» в ваш класс Particle. Они будут содержать в основном тот же код, что и выше, но они переопределяют (а не перегружают) глобальный «новый» оператор и, только для класса Particle, определяют специализированный распределитель, который заменяет глобальное «новое». Самое приятное, что пользователи объектов Particle даже не должны знать, что есть специальный распределитель; они просто используют «новое» и «удаляют» как обычно и остаются блаженно не осознающими, что их объекты Частицы исходят из специального заранее выделенного пула.

+0

Ваш код работает, но не могли бы вы объяснить, как хранится ваш связанный список? Как это перевести? '* ((Particle **) p) = p + 1;' – Ulterior

+0

@Ulterior - Cool ... Я на самом деле не тестировал его, поэтому я рад, что он работает. Вы такие же, как Grimless? Ключом к стратегии является то, что когда объекты Частицы не используются (т. Е. Они находятся в нераспределенном связанном списке), они функционируют как внутренний «следующий» указатель для списка. Для этого я бросаю их как указатели частиц (Particle *). Чтобы записать адрес объекта Particle в один, я навел указатель p как указатель на частицу * или Particle **, затем разыменовал его, чтобы присвоить ему Particle *. В этом конкретном случае я пишу в него адрес следующей Частицы, образуя связанный список. – phonetagger

+0

Эта конструкция работает до тех пор, как 'sizeof (Particle)> = sizeof (Particle *)'. Если Частицы меньше указателей, это терпит неудачу. –

0

Вам нужно будет написать свой собственный или найти того, кто уже написал их, и повторно использовать то, что они написали. Для управления этим сценарием не существует стандартной библиотеки 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. Возвращаемый дескриптор позволяет вам иметь отдельные распределения буфера для разных типов (или даже для одного и того же типа несколько раз). Функция destroy принудительно освобождает все пространство, связанное с дескриптором.

Функция распределения предоставляет вам новую единицу пространства для использования. Свободная функция освобождает это для повторного использования.

За кулисами код отслеживает, какие устройства используются (возможно, бит-карта) и может выделять дополнительное пространство по мере необходимости или может лишить пространство при первоначальном распределении. Вы могли бы организовать для него более или менее резко потерпеть неудачу, когда закончится свободное пространство (поэтому распределитель никогда не возвращает нулевой указатель). Ясно, что свободная функция может проверить, что указатель, который он задан, был таким, который был предоставлен ручкой-распределителем буфера, которая в настоящее время используется. Это позволяет обнаружить некоторые ошибки, которые обычный free() обычно не обнаруживает (хотя библиотека библиотеки GNU C malloc() и др., Похоже, выполняет некоторую проверку работоспособности, которую другие не обязательно выполняют).

+0

Согласен. Стоит отметить, что вы просто запрашиваете C-эквивалент C++ new [] и удаляете []. – djechlin

+0

Отредактировано Ответ намного лучше. Я боялся, что это будет так. Мне нужно будет разобраться в методах распределения кучи, чтобы реализовать это правильно. Скорее всего, OSS поможет решить эту точную проблему. Спасибо! – Grimless

0

Может быть, попробовать что-то вроде этого вместо того, чтобы ...

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); 
+0

Это точная ситуация Я стараюсь избегать: mallocing на основе частиц. Malloc может стать очень медленным, когда память начинает заканчиваться, и я теряю 24 байта для каждого malloc, тратя 24K на 1000 частиц системы. Обычно я пытаюсь эффективно размер моего буфера для повторного использования частиц без повторного удара malloc. – Grimless

+0

Это также неверно: particle [0] не является указателем. – wildplasser

+0

Изменены частицы в массив частиц *, но я думаю, что это спорная точка. – gpian

1

О, извините. Этот вопрос - это C, который я вижу. Не C++. Хорошо, если бы это было C++, это помогло бы вам.

Посмотрите на Boost's pool allocation library.

Мне кажется, что каждый из ваших распределений имеет одинаковый размер? Размер частицы, правильно? Если так, функции распределения пула из Boost будут работать очень хорошо, и вам не нужно писать свои собственные.

-1

Я создаю простую систему частиц и хочу использовать единственный буфер массива структур для управления моими частицами.

Я думаю, что вы ответили на него:

static Particle myParticleArray[numParticles]; 

получает выделено в начале программы и высвобождены в конце концов, просто. Или вам нравится ваш псевдокод и malloc массив сразу. Вы можете спросить себя, почему выделить одну частицу, почему бы не выделить всю систему? Напишите свои функции API, чтобы взять указатель на массив частиц и индекс.

+1

Это не решает мою проблему. Мне нужно уметь находить наилучшую доступную позицию в этом массиве частиц, чтобы порождать следующую испущенную частицу. Кроме того, если я отключу частицы или захочу иметь больше частиц на экране, этот массив не может быть изменен и не удален. Это явно противоречит моим целям и не требует времени и усилий для решения этой проблемы, прежде чем учитывать ее. – Grimless

+0

Привет, Grimless, так как вы сказали 'C' 'simple' и 'single array', я просто хотел представить альтернативу, которая не требовала бы дополнительной библиотеки или фантазии malloc/free. Похоже, что (из других комментариев и AFAIK) вам придется написать свой собственный malloc/free или найти lib вне стандарта. Из вашего описания (большого количества частиц) это звучало так, будто вы, возможно, хотели манипулировать коллекциями частиц, а не отдельными частицами. Я предположил, что вы пишете интерфейс частиц, поэтому вы можете написать его так: ParticleInit (частицы частиц * без знака); –

+0

Без намерения звучать бездумно. –