Переписывание многомерных VLA в C89

Я переписываю кучу C99 VLA для проекта с открытым исходным кодом для поддержки в Visual Studio. Итак, у меня много утверждений типа

void somefunc(double var[r]){...}

на который я переписываю

void somefunc(double *var) {
     var = malloc(sizeof(double) * r);
     ...
     free(var);
}

Что я предполагаю, что это правильный способ сделать это.
Проблема в том, что я начинаю видеть некоторые более сложные объявления VLA, которые ставят меня в тупик. Например:

double *(*var)[length1][length2][length3] = malloc(sizeof(double *[length4][length1][length2][length3]));

Так что для меня это по сути выглядит как 5-мерный массив. Это явно запрещено в C89 из-за использования переменных в функции sizeof().

Я предполагаю, как переписать это:

double *****var = malloc(sizeof(double ****) * length1);
      for(int i = 0; i<length1; i++) {
            var[i] = malloc(sizeof(double ***) * length2);
            for(int j = 0; j<length2; j++) {
                 var[i][j] = malloc(sizeof(double **) * length3);
                      for(int k = 0; k<length3; k++) {
                           var[i][j][k] = malloc(sizeof(double *) * length4);
                      } 
            }
      }

Как бы запутанно это ни было, это мое лучшее предположение о том, как переписать приведенное выше утверждение. Это технически правильно, и есть ли лучший способ сделать это?


person Ethan Pavolik    schedule 17.01.2016    source источник
comment
Если у вас есть это double *(*var)[length1][length2][length3] = malloc(sizeof(double *[length4][length1][length2][length3]));, вы должны переписать всю программу с самого начала.   -  person Iharob Al Asimi    schedule 18.01.2016
comment
Я предлагаю вам найти первоначальных разработчиков и расстрелять их, если только это не было шуткой с их стороны. А что касается объявления, то на самом деле это указатель на трехмерный массив из double указателей.   -  person user4520    schedule 18.01.2016
comment
@szczurcio Это имеет больше смысла, спасибо. Моя догадка о переписывании все еще работает?   -  person Ethan Pavolik    schedule 18.01.2016
comment
В C89 нет возможности буквально перекодировать многомерные массивы переменного размера. Вы можете поступить так, как это делает @szczurcio, и подделать массивы multi-d с массивами указателей. Но это имеет накладные расходы на пространство указателя и потенциально загрузку кеша. Более точная транскрипция заключается в определении функций и/или макросов, которые выполняют арифметические операции индексации в одномерном массиве и перекодируют все обращения к массиву. То есть для массива MxNxP A вы должны получить доступ к элементу A[i][j][k] как A1d[(i * N + j) * P + k]. Конечно, A1d выделяется M*N*P элементами. Из-за этой проблемы в язык были добавлены vla!   -  person Gene    schedule 18.01.2016
comment
Ваше преобразование первого примера (с double var[r]) почти наверняка неверно.   -  person user253751    schedule 18.01.2016
comment
Считается, что, вероятно, проще и продуктивнее написать плагин для VS, чтобы использовать правильный компилятор C вместо clang или MinGW версии gcc...   -  person user268396    schedule 18.01.2016
comment
Используйте подключаемый модуль компилятора Intel или Clang для MSVC вместо того, чтобы позволить Microsoft не поддерживать язык ISO (например, C99) разрушить вашу жизнь.   -  person Jeff Hammond    schedule 18.01.2016


Ответы (1)


Вот моя попытка:

double***** var = malloc(sizeof *var * length4);

for (int i = 0; i < length4; ++i)
{
    var[i] = malloc(sizeof *var[i] * length1);

    for (int j = 0; j < length1; ++j)
    {
        var[i][j] = malloc(sizeof *var[i][j] * length2);

        for (int k = 0; k < length2; ++k)
        {
            var[i][j][k] = malloc(sizeof *var[i][j][k] * length3);

            for (int l = 0; l < length3; ++l)
            {
                var[i][j][k][l] = NULL;     
                // var[i][j][k][l] is a double* - you can allocate some room for a double here, or assign the address of an existing variable
            }
        }
    }
}

Подобно вашему, за исключением того, что обратите внимание, как я использовал length4 в первом malloc. Если вы посмотрите на исходное объявление, var — это указатель на трехмерный массив double*. Как видно из вызова malloc справа, выделяется память для length4 таких массивов, поэтому var можно представить как массив из length4 трехмерных массивов из double*.

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

IIRC, объявляющий переменные внутри fors, недействителен C89, но вы можете переместить их выше в верхнюю часть своей области.

Обратите внимание, что, как указывает @immibis в комментариях, ваше первое преобразование, скорее всего, является ошибкой; double *var — это аргумент функции, что означает, что он передается по значению, поэтому любые изменения, которые вы вносите в него внутри функции, не видны снаружи, и, кроме того, вы malloc немного памяти, что-то с ней делаете, а затем free ее . Даже если вы модифицировали переданный указатель, что потребовало бы, чтобы тип параметра был double**, по-прежнему нет необходимости передавать переменную, поэтому вы можете использовать ее исключительно локально. Скорее всего, вы хотели malloc памяти за пределами функции и передать ей действительный указатель (и, надеюсь, размер).

person user4520    schedule 17.01.2016
comment
Есть ли разница между словами sizeof *var и sizeof(double ****) или это просто вопрос предпочтений? Спасибо за помощь. - person Ethan Pavolik; 18.01.2016
comment
@EthanPavolik Нет, это то же самое, это действительно зависит от вас (то же самое со скобками, sizeof(*var) также будет действительным`). - person user4520; 18.01.2016