2012-06-25 2 views
0

Пытается использовать один блок malloc() ed для хранения в нем нескольких структур разных типов. Участники являются фиктивными для демонстрационных целей. Не допускается никаких предположений о их типе, порядке и т. Д., За исключением того, что они составляют совершенно законные C-структуры, которые являются полными (полностью известными и определенными для компилятора C). На этот раз это так, в другое время. Я за концепцией. Создайте свой собственный микс, но идея должна быть такой же - структура заголовка имеет указатели на другие структуры:C: Один блок malloced для нескольких структур разных типов.

typedef struct 
{ 
    int a; 
} s1_t; 

typedef struct 
{ 
    int b; 
} s2_t; 

typedef struct 
{ 
    int c; 
} s3_t; 

typedef struct 
{ 
    s1_t* s1; 
    s2_t* s2; 
    s3_t* s3; 
} hdr_t; 

int 
main(int argc, char* argv[]) 
{ 
    void* mem = malloc(sizeof(hdr_t) + 
          sizeof(s1_t) + 
          sizeof(s2_t) + 
          sizeof(s3_t)); 

    hdr_t hdr = (hdr_t*)mem; 

    hdr->s1 = (s1_t*) (hdr + sizeof(hdr)); 
    hdr->s2 = (s2_t*) (hdr->s1 + sizeof(s1_t)); 
    hdr->s3 = (s3_t*) (hdr->s2 + sizeof(s2_t)); 

    /* etc. */ 
} 

Выше (наивный) код надежда не фрагментировать кучу с небольшим mallocs, а получить один блок для весь (известный в разное время).

Вопрос: Как можно в качестве переносного решения мне нужно вручную выровнять структуры в моей схеме?

Других слов мне нужен (псевдо-код), ALSZ = размер выравнивания для этого чипа:

mem = malloc(hdrsz + ALSZ + s1sz + ALSZ + s2sz + ALSZ + s3sz); 

hdr = (hdr_t*)hdr; 

s1 = align(hdr + sizeof(hdr_t), ALSZ); 

s2 = align(s1 + sizeof(s1), ALSZ); 

s3 = align(s2 + sizeof(s2), ALSZ); 

Align (MEM, граница) моя подпрограмма для вычисления выровненного адреса.

Спасибо за ваши мысли.

+0

Просто из любопытства: почему вы не можете использовать союз? Все это само по себе обойдется. – Jack

+0

Союзы - это способ интерпретировать одно местоположение памяти разными способами. Его шаблон использования является «одним из». Не собираюсь работать на меня. Моя модель использования «каждый». – user1478807

ответ

2

Вместо использования

typedef struct 
{ 
    s1_t* s1; 
    s2_t* s2; 
    s3_t* s3; 
} hdr_t; 

использования

typedef struct 
{ 
    s1_t s1; 
    s2_t s2; 
    s3_t s3; 
} hdr_t; 

main(int argc, char* argv[]) 
{ 
    void* mem = malloc(sizeof(hdr_t)); 

    hdr_t* hdr = (hdr_t*)mem; 
} 

Это достигается именно то, что вы ищете: прилежащее выделение памяти для набора структуры и гарантий того, что члены hdr_t являются указывая на правильное местоположение, не заботясь о дополнении выравнивания памяти чипов;

+1

+1 это приятное решение в случае, когда у вас есть фиксированное количество известных структур. –

0

Решение Dancrumb, безусловно, является лучшим, если вы можете его использовать. Если нет (например, если количество структур, в которых вы нуждаетесь, является переменным или слишком много комбинаций или специальный корпус является чрезмерно дорогостоящим), вы можете выполнить выравнивание самостоятельно:

Поскольку требование выравнивания любого типа всегда делит размер типа, начните с принятия LCM всех типов, которые могут отображаться в ваших структурах. Если вы хотите быть ленивым, просто возьмите продукт sizeof(short)*sizeof(int)*sizeof(long)*... для всех типов. Или просто используйте большую мощность в два, например 64, что вы уверены, больше, чем размер любого из них.

Затем при выполнении арифметики указателя на блоке, полученном malloc, округлите размер каждой структуры до следующего кратного значения выравнивания, которое вы выбрали выше. Например

#define ROUND_UP(n, a) (((n)+((a)-1))/(a)*(a)) 
ps2 = (void *) ((char *)ps1 + ROUND_UP(sizeof(*ps1), ALIGNMENT)); 

, где ps1 это указатель на первую структуру в блоке malloc'd и ps2 является указателем на второй.

Конечно, вам нужно сделать то же самое округление при распределении, чтобы обеспечить достаточное пространство.

Редактировать: Я нашел улучшение в этом в одном из моих старых ответов. Так как это боль, чтобы попытаться выяснить максимально возможное требование выравнивания, особенно если вы хотите быть на 100% переносной, просто используйте тот факт, что требование выравнивания любого типа (включая структуры) должно делить размер типа, и предположить, что они равны.То есть:

ps2 = (void *) ((char *)ps1 + ROUND_UP(sizeof(*ps1), sizeof(*ps2))); 

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

+0

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

+0

Некоторые более толстые пальцы. Гоша! Может ли кто-нибудь научить меня типу? Так или иначе. Спасибо всем за ваш вклад. Я думаю, что ответ Р. является самым близким к тому, что мне нужно. Подвести итог. Да. Мне нужно выровнять вещи вручную для большинства переносных решений. – user1478807

+0

Stackoverflow не позволяет мне выдвинуть предложение Р.. Итак, вот оно. +1 для R .. – user1478807

0

Ошибка вашей арифметики указателя. Добавление 1 к указателю не продвигает его на один байт, а на следующий элемент; по размеру остроконечного типа.

Если у вас есть

struct foo { 
    ... 
}; 

struct bar { 
    ... 
}; 

struct baz { 
    ... 
}; 

struct header { 
    ... 
    struct foo *foo; 
    struct bar *bar; 
    struct baz *baz; 
    ... 
}; 

можно выделить структуры с использованием одного блока памяти с помощью

struct header *ptr; 

ptr = malloc (sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar) + sizeof (struct baz)); 
if (!ptr) { 
    /* out of memory */ 
    exit(1); 
} 

ptr->foo = (struct foo *)(ptr + 1); 
ptr->bar = (struct bar *)(ptr->foo + 1); 
ptr->baz = (struct baz *)(ptr->bar + 1); 

В качестве альтернативы, если вы хотите работать с смещениях байтов (например, если размеры структуры динамичны - вполне законны в C99), вы можете использовать

struct header *ptr; 
char   *tmp; 

tmp = malloc (sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar) + sizeof (struct baz)); 
if (!tmp) { 
    /* out of memory */ 
    exit(1); 
} 

ptr = (struct header *)tmp; 
ptr->foo = (struct foo *)(tmp + sizeof (struct header)); 
ptr->bar = (struct bar *)(tmp + sizeof (struct header) + sizeof (struct foo)); 
ptr->baz = (struct baz *)(tmp + sizeof (struct header) + sizeof (struct foo) + sizeof (struct bar)); 

В обоих случаях случае указатели в структуре struct header получат те же значения относительно указателя самого struct header. В последнем случае вы также должны помнить о том, чтобы выровнять новые указатели в соответствии с требованиями ABI, если вы используете произвольные размеры структуры.

+0

Мой плохой. Я был не очень ясен. Когда я сказал «никаких предположений о членах структуры», я имел в виду, что они являются действительными C-структурами, но они определены в другом месте. Я не знаю их определений. Все, к чему у меня есть доступ - это настраиваемые подпрограммы S1SizeOf(), S2SizeOf() и т. Д. Я делаю mallocing, пользователи «вставляют» свои структуры там позже. Ответ Р. наиболее близок к тому, что я намереваюсь сделать. Извините за то, что вы не поняли с самого начала. В основном цель состоит в том, чтобы иметь одно место и достаточно места и хорошее выравнивание для пользовательских типов, которые известны только размерами через их пользовательские подпрограммы typeSizeOf(). – user1478807

+0

600 символов недостаточно! Anyaway. Одно место, достаточный размер и хорошее выравнивание, старые добрые C настраиваемые структуры, определенные в другом месте, а затем вставляемые в выделенные точки внутри malloced buffer. Я не знаю, что это такое, но я знаю, что это структуры. Отсюда исходный вопрос. Выровнять или не выровнять. – user1478807

Смежные вопросы