malloc() массива структур со структурами разного размера

Как правильно выделить массив структур, если каждая структура содержит массив строк разного размера?

Таким образом, каждая структура может иметь разный размер и сделать невозможным

realloc (число структур * размер (имя структуры))

после

malloc (начальный размер * sizeof (имя структуры)

Как выделить память для этого и следить за тем, что происходит?


person some_id    schedule 13.02.2011    source источник
comment
Размер каждой структуры фиксирован. Если у вас есть массив внутри структуры, который может выйти за пределы своего размера, это уже другая история, но компилятору все равно.   -  person user541686    schedule 13.02.2011
comment
Эта другая история - моя проблема и вопрос :)   -  person some_id    schedule 13.02.2011
comment
Как именно выглядит ваша структура?   -  person jswolf19    schedule 13.02.2011
comment
я не думаю, что он скомпилируется, если они будут разных размеров... это практически невозможно   -  person Scott M.    schedule 13.02.2011


Ответы (4)


Я делаю некоторые предположения, основанные на информации, которую вы предоставили. Единственная причина, по которой я вижу желание realloc массива структур, заключается в том, что вы хотите добавить больше структур в этот массив. Это классно. Есть много причин, чтобы хотеть такое динамическое хранилище. Лучший способ справиться с этим, особенно если структуры сами по себе являются динамическими, — сохранить массив указателей на эти структуры. Пример:

<сильный>1. Структура данных:

typedef struct {
    int numberOfStrings;
    char ** strings;
}
stringHolder;

typedef struct {
    int numberOfStructs;
    stringHolder ** structs;
}
structList;

<сильный>2. Управление динамическими массивами строк:

void createNewStringHolder(stringHolder ** holder) {
    (*holder) = malloc(sizeof(stringHolder));
    (*holder)->numberOfStrings = 0;
    (*holder)->strings = NULL;
}

void destroyStringHolder(stringHolder ** holder) {
    // first, free each individual string
    int stringIndex;
    for (stringIndex = 0; stringIndex < (*holder)->numberOfStrings; stringIndex++)
    { free((*holder)->strings[stringIndex]); }
    // next, free the strings[] array
    free((*holder)->strings);
    // finally, free the holder itself
    free((*holder));
}

void addStringToHolder(stringHolder * holder, const char * string) {
    int newStringCount = holder->numberOfStrings + 1;
    char ** newStrings = realloc(holder->strings, newStringCount * sizeof(char *));
    if (newStrings != NULL) {
        holder->numberOfStrings = newStringCount;
        holder->strings = newStrings;
        newStrings[newStringCount - 1] = malloc((strlen(string) + 1) * sizeof(char));
        strcpy(newStrings[newStringCount - 1], string);
    }
}

<сильный>3. Управление динамическим массивом структур:

void createNewStructList(structList ** list, int initialSize) {
    // create a new list
    (*list) = malloc(sizeof(structList));
    // create a new list of struct pointers
    (*list)->numberOfStructs = initialSize;
    (*list)->structs = malloc(initialSize * sizeof(stringHolder *));
    // initialize new structs
    int structIndex;
    for (structIndex = 0; structIndex < initialSize; structIndex++)
    { createNewStringHolder(&((*list)->structs[structIndex])); }
}

void destroyStructList(structList ** list) {
    // destroy each struct in the list
    int structIndex;
    for (structIndex = 0; structIndex < (*list)->numberOfStructs; structIndex++)
    { destroyStringHolder(&((*list)->structs[structIndex])); }
    // destroy the list itself
    free((*list));
}

stringHolder * addNewStructToList(structList * list) {
    int newStructCount = list->numberOfStructs + 1;
    size_t newSize = newStructCount * sizeof(stringHolder *);
    stringHolder ** newList = realloc(list->structs, newSize);
    if (newList != NULL) {
        list->numberOfStructs = newStructCount;
        list->structs = newList;
        createNewStringHolder(&(newList[newStructCount - 1]));
        return newList[newStructCount - 1];
    }
    return NULL;
}

<сильный>4. Основная программа:

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

int main (int argc, char * argv[]) {
    structList * allHolders;
    createNewStructList(&allHolders, 10);

    addStringToHolder(allHolders->structs[4], "The wind took it");
    addStringToHolder(allHolders->structs[4], "Am I not merciful?");
    addStringToHolder(allHolders->structs[7], "Aziz, Light!");

    printf("%s\n", allHolders->structs[4]->strings[0]);  // The wind took it
    printf("%s\n", allHolders->structs[4]->strings[1]);  // Am I not merciful?
    printf("%s\n", allHolders->structs[7]->strings[0]);  // Aziz, Light!

    stringHolder * newHolder = addNewStructToList(allHolders);
    addStringToHolder(newHolder, "You shall not pass!");
    printf("%s\n", newHolder->strings[0]);               // You shall not pass!
    printf("%s\n", allHolders->structs[10]->strings[0]); // You shall not pass!

    destroyStructList(&allHolders);
    return 0;
}
person e.James    schedule 13.02.2011
comment
Для записи: приведенный выше код будет скомпилирован и запущен. Я не выдумываю! :) - person e.James; 13.02.2011

Если в вашей структуре есть char *, она занимает размер одного указателя. Если у него есть char[200], он занимает двести байт.

person Victor    schedule 13.02.2011
comment
Чтобы добавить к этому ответу, выделение данных (строки), на которые будет указывать указатель внутри структуры (если это char *, а не char[...]), отделено от выделения массива структур. - person Jeremiah Willcock; 13.02.2011

Как правило, нет. Есть две причины, по которым вы можете это сделать:

  1. Так что один free() освободит весь блок памяти.
  2. Чтобы избежать фрагментации внутренней памяти.

Но если у вас не исключительная ситуация, ни один из них не является очень убедительным, потому что у этого подхода есть существенный недостаток:

Если вы это сделаете, то block[i] бессмысленно. Вы не выделили массив. Невозможно сказать, где начинается ваша следующая структура, не изучив структуру или не имея внешней информации о размере/положении ваших структур в блоке.

person rlibby    schedule 13.02.2011

Не очень понятно, как объявляется ваш тип struct. C99 имеет специальную конструкцию для таких вещей, называемую гибким членом массива struct:

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

Вы можете сделать что-то вроде

typedef struct myString myString;
struct myString { size_t len; char c[]; };

Затем вы можете выделить такого зверя с

size_t x = 35;
myString* s = malloc(sizeof(myString) + x);
s->len = x;

и перераспределить его с помощью

size_t y = 350;
{
  myString* tmp = realloc(s, sizeof(myString) + y);
  if (!tmp) abort(); // or whatever
  tmp->len = y;
}
s = tmp;

Чтобы использовать это более удобно, вам, вероятно, лучше обернуть это в макросы или встроенные функции.

person Jens Gustedt    schedule 13.02.2011