C - преобразовать массив элементов в двумерную матрицу

Это может быть глупый вопрос, но мне интересно, есть ли эффективный способ сделать это.

Ситуация:

int* array = malloc(n * m * sizeof(int));
//want to convert array into M[n][m]

что я делаю сейчас:

int** M = malloc(n * sizeof(int*));
for(int i = 0; i < n; i++, array += m)
  M[i] = array; 

Я не думаю, что преобразование должно быть таким сложным. Есть ли простой синтаксис C? Могу ли я объявить extern M[n][m], а затем установить его адрес в массив?

(обработка ошибок и управление памятью в примере опущены для простоты. Просто представьте, что это часть какой-то функции.)

Да ладно, это C, наведите указатель на то, что вы хотите

smac89 21.01.2019 03:14

@ smac89 Я хочу использовать синтаксис индекса M [a] [b] по какой-то другой причине.

Yi Lin Liu 21.01.2019 03:15

@ smac89 smac89 могу ли я привести его к типу int [n] [m]?

Yi Lin Liu 21.01.2019 03:17

Кажется, здесь работает: coliru.stacked-crooked.com/a/6790414229295052

smac89 21.01.2019 03:32

@ smax89 вау, это намного динамичнее, чем я себе представлял. Как размер распознается во время выполнения? Это удивительно.

Yi Lin Liu 21.01.2019 03:45

@YiLinLiu. Это не. Вы передаете его. Двумерные массивы должны иметь указанные первые размеры, потому что компилятор вычисляет для вас линейное смещение. Это во время выполнения в том смысле, что используемые вами переменные являются динамическими, но выражение смещения вычисляется во время компиляции.

Mad Physicist 21.01.2019 04:09

@MadPhysicist, вы имеете в виду, что компилятор отследил его значение? Что делать, если функции настолько сложны, что компилятор не может сказать об этом?

Yi Lin Liu 21.01.2019 04:12

@MadPhysicist, я как раз собирался прокомментировать то же, что и выше. Как он вычисляется в этом случае: coliru.stacked-crooked.com/a/39e6761edaccd062? Здесь размер не известен до времени выполнения

smac89 21.01.2019 04:13

@ smac89 так же, как вы вычисляете i + sp * j для любых других переменных. Когда вы говорите int (*p)[sp], это означает, что для данного i, j умножьте j на sp перед добавлением к i и использованием в качестве смещения. Все, что делает компилятор, — это преобразует вычисленное вручную линейное смещение в причудливый синтаксис, который легче читать.

Mad Physicist 21.01.2019 04:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
1 105
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Используйте массив указателей.

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    int n = 3, m = 4, i, j, count=0;

    int *array[n];
    for(i=0; i<n; i++)
     array[i] = (int *)malloc(m * sizeof(int));
     if ( array[i] == NULL)
     {
        perror("Unable to allocate array");
        exit(1);
     }

    // going to add number to your 2d array.

    for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         array[i][j] = ++count;

    for (i=0; i < n; i++)
      for (j=0; j < m; j++)
         printf("%d ", array[i][j]);

      // free memory

      for(i=0; i<n; i++)
         free(array[i]);

     }

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

Jonathan Leffler 21.01.2019 04:00

Дело в том, что изначально я получил массив (а не массив указателей), с чем бы мне не хотелось возиться.

Yi Lin Liu 21.01.2019 04:00

Хорошо, я освободил память.

Manny_Mar 21.01.2019 04:10

Вы можете просто использовать 2d-массив и затем элементы, просто array[m][n] и использовать цикл for для помещения элементов в массив. malloc не нужен

Manny_Mar 21.01.2019 04:12

В то время как хороший общий подход, как правило, по моему ограниченному опыту, два уровня косвенности, как правило, немного медленнее, чем вычисление линейного индекса. И у вас не должно быть проблем с синтаксисом в свете этого комментария: stackoverflow.com/questions/54282732/…

Mad Physicist 21.01.2019 04:15

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

В C вы должны помнить, что массив на самом деле просто указатель. Когда вы запрашиваете int *array = malloc(n * sizeof(int)), вы говорите компилятору, что вам нужно n много 4-байтовых блоков типа int, зарезервированных рядом в памяти, где значение array на самом деле является указателем на первый 4-байтовый блок.

Когда вы получаете доступ к элементам массива, вы на самом деле выполняете арифметику указателя и разыменовываете указатель, но это скрыто в синтаксисе array[i]. Таким образом, когда массив имеет тип int, array[2] переводится как переход к местоположению, указанному указателем массива (т.е. головкой), теперь перемещает 2 * 4 байта в памяти и разыменовывает указатель для доступа к хранящемуся там целому числу.

Поэтому, когда вы создаете двумерный массив, как вы обсуждали, на самом деле нет лучшего способа сделать это. Убедитесь, что у вас есть твердое представление о том, что на самом деле вы получаете от компилятора. Указатели (во всяком случае, на 64-битных машинах) 8 байтов, а целые 4 байта. Итак, когда вы вызываете int **M = malloc(sizeof(int*) * m, компилятор выделяет вам m блоков шириной 8 байт каждый, каждый из которых имеет тип int*.

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

3 полезных совета:

  • calloc(n, sizeof(int)) может быть лучше, чем вызов malloc, потому что calloc автоматически инициализирует ваши записи нулем, а malloc — нет.
  • при вызове calloc/malloc вы хотите убедиться, что динамическое выделение памяти прошло успешно; если это не удастся, то malloc вернет NULL.
  • Хорошее эмпирическое правило состоит в том, что каждый раз, когда вы вызываете malloc, вы хотите вызвать free, как только закончите с памятью. Это может помочь предотвратить утечку памяти.

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

Yi Lin Liu 21.01.2019 03:57

Можно утверждать, что int *array = malloc(…); не может быть глобальной переменной, потому что в C вы не можете использовать результат вызова функции для инициализации глобальной (или статической) переменной.

Jonathan Leffler 21.01.2019 04:05
Ответ принят как подходящий

После:

int* array = malloc(n * m * sizeof(int));

ты можешь сделать:

int (*M)[m] = (int(*)[m])array;

а затем используйте, например, M[1][2].

Вы могли бы сделать это и в первую очередь:

int (*M)[m] = malloc( n * sizeof *M );

Сложная часть заключается в объявлении переменной для хранения указателя на выделенный массив; остальное прямолинейно — при условии, что у вас есть компилятор C99 или более поздней версии.

#include <stdio.h>
#include <stdlib.h>

static void print_2dvla(int rows, int cols, int data[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        printf("%2d: ", i);
        for (int j = 0; j < cols; j++)
            printf(" %4d", data[i][j]);
        putchar('\n');
    }
}

int main(void)
{
    int m = 10;
    int n = 12;

    int (*M)[m] = malloc(n * m * sizeof(M[0][0]));
    if (M == NULL)
    {
        fprintf(stderr, "Failed to allocate %zu bytes memory\n", n * m * sizeof(M[0][0]));
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
            M[i][j] = (i + 1) * 100 + (j + 1);
    }

    print_2dvla(n, m, M);

    free(M);
    return 0;
}

Пример вывода:

 0:   101  102  103  104  105  106  107  108  109  110
 1:   201  202  203  204  205  206  207  208  209  210
 2:   301  302  303  304  305  306  307  308  309  310
 3:   401  402  403  404  405  406  407  408  409  410
 4:   501  502  503  504  505  506  507  508  509  510
 5:   601  602  603  604  605  606  607  608  609  610
 6:   701  702  703  704  705  706  707  708  709  710
 7:   801  802  803  804  805  806  807  808  809  810
 8:   901  902  903  904  905  906  907  908  909  910
 9:  1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
10:  1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
11:  1201 1202 1203 1204 1205 1206 1207 1208 1209 1210

Ключевая строка:

int (*M)[m] = malloc(n * m * sizeof(M[0][0]));

Это говорит о том, что M является указателем на массив массивов int, каждый из которых имеет размерность m. Остальной код просто использует этот массив с обычной записью с двумя нижними индексами — M[i][j] и т. д. Его можно передавать в функции. Я не показывал это здесь, но также тривиально поместить код инициализации в функцию, а затем иметь несколько разных размеров матрицы в одной функции.

Другие вопросы по теме