Попытка динамически выделить многомерный массив в C приводит к сбою

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

t.versions=(char***)malloc(sizeof(char**)*4);
t.versions[0]=(char**)malloc(sizeof(char*)*t.size*4);
t.versions[0][0]=(char*)calloc(t.size*t.size*4,sizeof(char));
for (i=1; i<t.size*4; ++i) 
    t.versions[0][i]=t.versions[0][i-1]+t.size;
for (i=1; i<4; ++i) 
    t.versions[i]=t.versions[i-1]+t.size;

Помимо других преимуществ, это решение упрощает освобождение выделенной памяти:

void contiguous_array_free(void** ptr, int depth)
{
    int *ptr_d;
    ptr_d=(int*)*ptr;
    if (depth>1)
        contiguous_array_free((void**)ptr_d, depth-1);
    free(ptr);
}
//(elsewhere in the code)
contiguous_array_free((void**)(*tile).versions, 3);

Теперь у меня возникла небольшая проблема с выделением этих массивов. Хотя описанный выше подход действительно работает, в идеале я хотел бы иметь универсальное решение, позволяющее распределять эти массивы с помощью одной функции. вызов.

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

//dimension points to a 1-dimensional array of integers
//specifying the size in each array dimension
void* contiguous_array_alloc(int* dimension, int depth, int size)
{
    int i;
    char** ptr;
    if (depth==1)
    {
        ptr=(char**)malloc(*dimension*size);
        return ptr;
    }
    ptr=(char**)malloc(*dimension*sizeof(char*));
    *(dimension+1)*=*dimension;
    ptr[0]=(char*)contiguous_array_alloc(dimension+1, depth-1, size);
    *(dimension+1)/=(*dimension);
    for (i=1; i<*dimension; ++i)
        ptr[i]=ptr[i-1]+(*(dimension+1)*size);
    return (void*)ptr;
}

//(later in the code) (
int dimension[3];
dimension[0]=4;
dimension[1]=t.size;
dimension[2]=t.size;
t.versions=(char***)contiguous_array_alloc(&dimension[0], 3, sizeof(char));

Добавление некоторых отладочных сообщений в код, по-видимому, указывает на то, что элементы распределены правильно:

Выделение массива [4][9][9] элементов размера 1; malloc() с 16-байтовым массивом для 4 указателей; Выделенный массив указателей на уровень 2 по адресу 003E29E8;

Выделение массива [36][9] элементов размера 1; malloc() с 144-байтовым массивом для 36 указателей; Выделенный массив указателей на уровень 1 по адресу 003E5728;

Выделение массива [324] элементов размера 1;

324-байтовый массив данных по адресу 003E57C0; Указал данные на 003E57C0; увеличение каждого указателя на 9; Возврат выделенного массива;

Указал данные на 003E5728; увеличение каждого указателя на 9; Возврат выделенного массива;

Выделенный непрерывный массив по адресу 003E29E8;

Что вызывает такое поведение? Я проверил код несколько раз и понятия не имею, что я сделал неправильно.


person Michał Gawlas    schedule 27.02.2013    source источник
comment
Я хотел бы размещать многомерные массивы в смежных участках памяти Насколько я знаю, вы не можете решить, что это зависит от ОС.   -  person m0skit0    schedule 27.02.2013
comment
Обязательно...   -  person Mysticial    schedule 27.02.2013
comment
Почему вы изменяете массив dimension?   -  person WhozCraig    schedule 27.02.2013
comment
Это отвечает на вопрос хотя и не объясняет проблемы, характерные для этого поста.   -  person Lundin    schedule 27.02.2013
comment
m0skit0, если быть педантичным: я знаю, что ближе всего к своей цели я могу сократить количество вызовов malloc() до n (где n — количество измерений в массиве). WhozCraig, как я отметил в комментарии, представляет собой одномерный массив целых чисел, содержащий размер желаемого массива в каждом измерении. Предполагаемый результат функции в этом примере — массив [4][t.size][t.size] из char.   -  person Michał Gawlas    schedule 27.02.2013
comment
Можете ли вы показать SSCCE (Короткий, автономный, правильный пример) с подробным описанием ваших структур, который вызывает выделение, инициализирует данные, печатает данные и освобождает данные? Это выглядит так, как будто вы выделяете трехмерный массив из отдельных символов; это правильно?   -  person Jonathan Leffler    schedule 27.02.2013
comment
Ваши верхний уровень и промежуточные массивы указателей настроены неправильно. Они указывают на массивы указателей, а не на то, что содержит ваши данные. Расстояние между ними на something*sizeof(yourdata) явно является ошибкой.   -  person n. 1.8e9-where's-my-share m.    schedule 27.02.2013


Ответы (2)


Я думаю, что что-то не так для ptr[i]=ptr[i-1]+(*(dimension+1)*size); такого рода использование операций с указателями не имеет смысла. Я изменил код, как показано ниже, который прошел тест для 4-мерного массива.

//dimension points to a 1-dimensional array of integers
//specifying the size in each array dimension
void* contiguous_array_alloc(int* dimension, int depth, int size) {
  int i;
  if (depth==2) {
    char ** ptr=(char **)malloc(*dimension * sizeof(void*));
    ptr[0]=(char *)malloc(*dimension * dimension[1] * size);
    for (i=1; i<*dimension; ++i) {
      ptr[i]=ptr[i-1]+(*(dimension+1) * size);
    }
    return (void*)ptr;
  } else {
    void ***ptr=(void***)malloc(*dimension * sizeof(void*));
    *(dimension+1)*=(*dimension);
    ptr[0]=contiguous_array_alloc(dimension+1, depth-1, size);
    *(dimension+1)/=(*dimension);
    for (i=1; i<*dimension; ++i) {
      ptr[i]=ptr[i-1]+(*(dimension+1));
    }
    return (void*)ptr;
  }
}
person fLOyd    schedule 27.02.2013
comment
Спасибо. Здесь действительно была проблема: я забыл, что p+x (где p — указатель, а x — целое число) равно p[x], а не x-му байту после p. - person Michał Gawlas; 27.02.2013

Все, что вам нужно для массива abcd из int, это:

int (*p)[b][c][d] = calloc(a, sizeof *p);
person Eric Postpischil    schedule 27.02.2013
comment
Я думаю, что вы в чем-то правы. Но дизайн многомерного массива Михала Гавласа прост в использовании, когда вы хотите передать его функциям, отличным от тех, где вы его вызвали. Доступ может осуществляться только путем вычисления памяти в другой функции, если вы просто назначаете непрерывную память. По контракту в этой реализации вы можете просто использовать какой-то стиль ptr[i][j][k] для доступа к элементу в любом месте. - person fLOyd; 27.02.2013
comment
@flOyd: C поддерживает массивы переменной длины. Вы можете передать указатель и получить к нему доступ в других функциях, передав его в таких параметрах: void /* or other type*/ foo(size_t b, size_t c, size_t d, int (*p)[b][c][d], /* other parameters */). Просто не нужна эта ерунда про многоуровневые указатели. - person Eric Postpischil; 27.02.2013
comment
Спасибо, что указали на это - я избегал этого, потому что помнил, что это не сработало, когда я пытался сделать это однажды. - person Michał Gawlas; 02.03.2013