Динамически установить шаблон доступа к массиву в C - PullRequest
2 голосов
/ 15 сентября 2011

Я хотел бы сделать что-то подобное в C (99):

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float matrices[][n][n] = malloc( sizeof(float) * n*n* T ); //[sic!]

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

/* Load each matrix with the unit matrix */
for( int t = 0; t < T; t++ )
for( int i = 0; i < n; i++ )
for( int j = 0; j < n; j++ )
    matrices[t][i][j] = (i==j)?1.:0.;

Конечно, я мог бы просто выделить «матрицы» как одномерный массиви использовать некоторую магию индекса, но очевидно, что приведенный выше шаблон доступа к массиву более удобен.В частности, для компилятора это не техническая чёрная магия, за исключением некоторых накладных расходов на запоминание размеров динамического массива.Существует ли расширение компилятора GCC, какая-либо функция диалекта или аналогичная, которая позволяет мне делать что-то подобное выше?

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

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float* matrices = malloc( sizeof(float) * n*n* T ); //[sic!]
for( int t = 0; t < T; t++ )
for( int i = 0; i < n; i++ )
for( int j = 0; j < n; j++ )
    matrices[t*n*n + i*n + j] = (i==j)?1.:0.;

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

Ответы [ 2 ]

2 голосов
/ 15 сентября 2011

Лучшее, что я нашел

Сохраняет атрибуты размера массива матриц (но не из списка * матриц *) и позволяет использовать нотацию индексирования

  int n=5, T=2;
  float (*a)[n][n] = calloc(T,sizeof(float[n][n])); // <== initializes to 0!
  for (size_t t=0; t<T; ++t) 
    for (size_t i=0; i<n; ++i)
      a[t][i][i] = 1.0;   // <== only set the 1's

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

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

void foo(int n, float (*a)[n][n]) {
  // ...
}

void bar(int t, int n, float a[t][n][n]) {
  // ...
}

работа. (На самом деле gcc позволит вам избежать неприятностей, если вы не используете -std=c89 -pendantic ...)

Второй лучший вариант, но он будет работать с ansi-c

Конечно, вы можете сделать традиционную версию (с уродливым индексированием рук) более легкой для чтения.

int n = compute_size_of_matrices();
int T = compute_number_of_matrices();
float* matrices = calloc(T,  sizeof(float) * n*n); // <== initializes to 0!
for( int t = 0; t < T; t++ )
   for( int i = 0; i < n; i++ )
    matrices[t*n*n + i*n + i] = 1;  // <== only set the 1's

Ну, это казалось хорошей идеей ...

Увы, c не позволит вам сделать

  int n=5, T=2;
  float matrices[T][n][n] = {};  // <== ***ERROR!!!***

, который позволил бы вам сохранить "массивность" матриц и быть еще яснее.

Это медленно?

Поскольку calloc будет использовать какой-либо сильно оптимизированный модуль записи системной памяти, чтобы установить в 0, вы не получите большой скорости удара.

0 голосов
/ 15 сентября 2011

RE: отсутствует размер в первом направлении

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

пример:

int arr[] = { 0,1 };             // arr size is 2
int arr[][2] = { {0,1}, {0,1} }; // arr size is 2,
                                 // each element points to an array of size 2

и т.д ...

Другой случай:

float * arr = calloc(float, sizeof(float) * n);

Здесь вы выделили непрерывную память для n чисел с плавающей запятой и arr указывает на нее. Вы можете получить доступ к элементам по арифметике указателя или по индексу массива, который сделает арифметику указателя за вас.

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

Компилятор не запоминает размер, если вы используете индексы массива, которые выходят за пределы массива, к которому вы обращаетесь к памяти, которая не является частью массива ...

...