Ошибка сегментации при попытке объявить массив 5D в программе C

#c #memory #fortran #segmentation-fault

#c #память #fortran #ошибка сегментации

Вопрос:

Я конвертирую программу с fortran77 на язык программирования C, в программе fortran объявляется массив 5D, как показано ниже, и код компилируется и выполняется хорошо. Но когда я преобразую тот же код в C с теми же значениями, код C компилируется, но выдает ошибку сегментации.

Если эта проблема связана с разным стандартным распределением памяти многомерных массивов между двумя языками, я перепробовал все комбинации измерений при объявлении массива на C, и во всех случаях это также дает «ошибку сегментации».

Я также устал от метода ‘calloc’, показанного ниже, и он работает, но я не знаю, как присваивать значения глобально объявленному массиву.

Примечание: ошибка сегментации происходит на этапе объявления (т. Е. Программа останавливается на этом)

Итак, в основном мои вопросы: — Почему это работает на fortran, но не на C?, и, — Как решить эти проблемы на C?

  ! Fortran77 Code
  parameter (i2maxbin=38)     
  parameter (imaxbin=20)       
  parameter (Nid=10)      
  real*8 dNdpt(Nid,i2maxbin,imaxbin,imaxbin,imaxbin)
  

  /* C Code */
  const int i2maxbin = 38;
  const int imaxbin = 20;
  const int nID = 10;
  double dNdpt[nID][i2maxbin][imaxbin][imaxbin][imaxbin];

 /* Declaring using calloc */
double (*dNdpt)[nID][i2maxbin][imaxbin][imaxbin][imaxbin] = 
calloc(sizeof(*dNdpt), 38);

for(int i = 0; i < nID; i  )
{
    for(int j = 0; j < i2maxbin; j  )
    {
        dNdpt[i][j][0][0][0] = 12.22673423;
    }        
}

When executing it gives me this error

error: assignment to expression with array type
         dNdpt[i][j][0][0][0] = 12.22673423;
                              ^
  

Комментарии:

1. Код C пытается использовать VLA (массивы переменной длины) — понятия не имею о Fortran. VLA типично реализованы в стеке; и C, как правило, имеет небольшое (иш) пространство стека. Вместо const int попытки #define .

2. Невозможно не скомпилировать и выдать segfault. Что происходит?

3. Наиболее вероятная причина заключается в том, что система не позволяет вам использовать много памяти. Его можно изменить (по крайней мере, в GCC), установив некоторые параметры в компиляции. Google (или duckduckgo лучше :)) mcmodel= medium

4. Этот массив будет занимать более 20 МБАЙТ пространства. Локальные нестатические переменные (также известные как автоматические переменные), как обычно, хранятся в (очень ограниченном) стеке. В Windows размер стека по умолчанию для процесса составляет всего один мегабайт, 5% от того, что необходимо для вашего массива.

5. В чем причина ` / * Объявления с использованием calloc */ double (* dNdpt) [nID] [i2maxbin] [imaxbin] [imaxbin] [imaxbin] = calloc(sizeof(* dNdpt), 38);` Вы уже создали переменную, в double dNdpt[nID][i2maxbin][imaxbin][imaxbin][imaxbin]; которой вам нужно только присвоить значение 0для элементов, 5-уровневый вложенный цикл, для правильной инициализации (если это еще не сделано автоматически).

Ответ №1:

следующий предлагаемый код:

  1. помещает массив в область действия файла, а не в стек
  2. чистая компиляция
  3. позволяет избежать использования динамического выделения / освобождения памяти
  4. чисто запускается и завершается без сбоев
  5. позволяет избежать использования VLAS

А теперь предлагаемый код:

 #define i2maxbin  38
#define imaxbin   20
#define nID       10



double dNptr[nID][i2maxbin][imaxbin][imaxbin][imaxbin];


int main( void )
{
    for(int i = 0; i < nID; i  )
    {
        for(int j = 0; j < i2maxbin; j  )
        {
            dNptr[i][j][0][0][0] = 12.22673423;
        }
    }
}
  

Ответ №2:

Размер стека в C, а также в C очень ограничен, как правило, допускается 1D-массив максимального размера 1e5, и если мы примем «двойной» размер равным 8 байтам, то это приведет к максимальной памяти в 8 * 1e5 байт.

Теперь давайте посмотрим на ваш 5D-массив, он пытается выделить 10*38*20*20*20 = 3,040,000 » двойные» единицы измерения, которые при преобразовании в память составляют 24 320 000 байт, что значительно превышает выделенный размер стека в C или C . Вот почему вы получаете ОШИБКУ SEG.

Вы можете попробовать инициализировать массив глобально, чтобы таким образом ему выделялась память кучи, которая обычно больше, чем память стека, даже тогда максимальный размер ограничен 8 * 7 * 1e5 байтами при макс. (зависит от вашей машины).

Комментарии:

1. Речь идет о переменных двойной точности (8 байт), то есть 24 320 000 байт. Размер стека в Windows немного ограничен (но может быть увеличен), в Linux он зависит от настройки ограничения (u). Я не думаю, что 24 МБ слишком велики для стека, но не очень. Вероятно, существует опция компилятора для размещения массивов такого типа в куче.

Ответ №3:

Вы решаете эту проблему, создавая массив в куче вместо стека.

 double* dNdpt = malloc(sizeof(double)*nID*i2maxbin*imaxbin*imaxbin*imaxbin);
  

затем отслеживайте, где вы находитесь в этом гигантском массиве, вычисляя индекс

более простой пример :

 double* my2dim = malloc(sizeof(double)*10*20); // 10 rows 20 cols

my2dim[3][2] = 10.0 would be written *(my2dim   3 * 20   2) = 10.0;