альтернатива многомерному массиву в c - PullRequest
4 голосов
/ 07 января 2012

t У меня есть следующий код:

 #define FIRST_COUNT 100
 #define X_COUNT 250
 #define Y_COUNT 310
 #define z_COUNT 40

struct s_tsp {

     short abc[FIRST_COUNT][X_COUNT][Y_COUNT][Z_COUNT];
};

struct s_tsp xyz;

Мне нужно просмотреть данные, подобные этим:

for (int i = 0; i < FIRST_COUNT; ++i)
    for (int j = 0; j < X_COUNT; ++j)
          for (int k = 0; k < Y_COUNT; ++k)
                for (int n = 0; n < Z_COUNT; ++n)
                      doSomething(xyz, i, j, k, n);

Я пытался придумать более элегантный, менее мозговоймертвый подход.(Я знаю, что такого рода многомерный массив неэффективен с точки зрения использования процессора, но это не имеет значения в этом случае.) Есть ли лучший подход к тому, как я здесь структурировал вещи?

Ответы [ 2 ]

5 голосов
/ 07 января 2012

Если вам нужен массив 4D, то это то, что вам нужно.Можно «сплющить» его в одномерный malloc() ed 'массив', однако это не совсем так:

abc = malloc(sizeof(short)*FIRST_COUNT*X_COUNT*Y_COUNT*Z_COUNT);

Доступ также сложнее:

*(abc + FIRST_COUNT*X_COUNT*Y_COUNT*i + FIRST_COUNT*X_COUNT*j + FIRST_COUNT*k + n)

Так что это, очевидно, немного болезненно.

Но у вас есть преимущество в том, что если вам нужно просто выполнить итерацию по каждому отдельному элементу, вы можете сделать:

for (int i = 0; i < FIRST_COUNT*X_COUNT*Y_COUNT*Z_COUNT; i++) {
    doWhateverWith *(abc+i);
}

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

2 голосов
/ 07 января 2012

ПРИМЕЧАНИЕ. Примеры, используемые в этом посте, предназначены только для объяснения понятий.Таким образом, примеры могут быть неполными, могут отсутствовать обработка ошибок и т. Д.

Когда дело доходит до использования многомерного массива в C, ниже приведены два возможных способа.

Уплощение массивов

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

Например,

int arr[10][10];
int *ptr = (int *)arr ;  

ptr[11] = 10; 
// this is equivalent to arr[1][0] = 10; assign a 2D array 
// and manipulate now as a single dimensional array. 

Техника использования смежной природы массивовизвестен как flattening of arrays.

рваные массивы

Теперь рассмотрим следующий пример.

char **list;
list[0] = "United States of America";
list[1] = "India";
list[2] = "United Kingdom";
for(int i=0; i< 3 ;i++)
    printf(" %d ",strlen(list[i]));
// prints 24 5 14

Этот тип реализации известен как рваный массив и полезен в местах, где используются строки переменного размера.Популярный метод - иметь dynamic-memory-allocation для каждого измерения.

ПРИМЕЧАНИЕ. Аргумент командной строки (char *argv[]) передается только как рваный массив.

Сравнение уплощенных и неровных массивов

Теперь давайте рассмотрим следующий фрагмент кода, который сравнивает массивы flattened и ragged.

/* Note: lacks error handling */
int flattened[30][20][10];
int ***ragged;
int i,j,numElements=0,numPointers=1;
ragged = (int ***) malloc(sizeof(int **) * 30);
numPointers += 30;
for( i=0; i<30; i++) {
    ragged[i] = (int **)malloc(sizeof(int*) * 20);
    numPointers += 20;
    for(j=0; j<20; j++) {
        ragged[i][j]=(int*)malloc(sizeof(int) * 10);
        numElements += 10;
    }
}

printf("Number of elements = %d",numElements);
printf("Number of pointers = %d",numPointers);

// it prints
// Number of elements = 6000
// Number of pointers = 631 

Из приведенного выше примера для массивов ragged требуется 631-pointers, другими словами, 631 * sizeof(int *) дополнительные ячейки памяти для указания 6000 целых чисел.Принимая во внимание, что массиву flattened требуется только один базовый указатель: то есть имя массива, достаточное для указания на смежные 6000 области памяти.

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

Старший ряд и главный столбец массивов

C следует row-major упорядочение для многомерных массивов,Flattening массивов можно рассматривать как эффект из-за этого аспекта в C.Значение row-major порядка C в том, что он соответствует естественному способу, которым большая часть доступа осуществляется в программировании.Например, давайте рассмотрим пример обхода матрицы N * M 2D,

for(i=0; i<N; i++) {
    for(j=0; j<M; j++)
        printf(“%d ”, matrix[i][j]);
    printf("\n");
}

Доступ к каждой строке в матрице один за другим путем быстрого изменения столбца.Массив C упорядочен в памяти таким естественным образом.Напротив, рассмотрим следующий пример:

for(i=0; i<M; i++) {
    for(j=0; j<N; j++)
        printf(“%d ”, matrix[j][i]);
    printf("\n");
}

Это меняет индекс столбца чаще, чем индекс строки.И из-за этого существует большая разница в эффективности между этими двумя фрагментами кода.Да, первый более эффективен, чем второй!

Поскольку первый обращается к массиву в естественном порядке (порядок row-major) C, следовательно, он быстрее, тогда как второму требуется больше времени для прыжка.Разница в производительности будет увеличиваться по мере увеличения числа измерений и размера элемента.

Так что при работе с многомерными массивами в C хорошо рассмотреть приведенные выше детали!

...