Пренаписване на многоизмерни 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 и да фалшифицирате мулти-d масиви с масиви от указатели. Но това има допълнителни разходи за пространство на показалеца и потенциално натоварване на кеша. По-вярната транскрипция е да се дефинират функции и/или макроси, които да извършват аритметиката на индексиране в 1-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