Это может быть глупый вопрос, но мне интересно, есть ли эффективный способ сделать это.
Ситуация:
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], а затем установить его адрес в массив?
(обработка ошибок и управление памятью в примере опущены для простоты. Просто представьте, что это часть какой-то функции.)
@ smac89 Я хочу использовать синтаксис индекса M [a] [b] по какой-то другой причине.
@ smac89 smac89 могу ли я привести его к типу int [n] [m]?
Кажется, здесь работает: coliru.stacked-crooked.com/a/6790414229295052
@ smax89 вау, это намного динамичнее, чем я себе представлял. Как размер распознается во время выполнения? Это удивительно.
@YiLinLiu. Это не. Вы передаете его. Двумерные массивы должны иметь указанные первые размеры, потому что компилятор вычисляет для вас линейное смещение. Это во время выполнения в том смысле, что используемые вами переменные являются динамическими, но выражение смещения вычисляется во время компиляции.
@MadPhysicist, вы имеете в виду, что компилятор отследил его значение? Что делать, если функции настолько сложны, что компилятор не может сказать об этом?
@MadPhysicist, я как раз собирался прокомментировать то же, что и выше. Как он вычисляется в этом случае: coliru.stacked-crooked.com/a/39e6761edaccd062? Здесь размер не известен до времени выполнения
@ smac89 так же, как вы вычисляете i + sp * j для любых других переменных. Когда вы говорите int (*p)[sp], это означает, что для данного i, j умножьте j на sp перед добавлением к i и использованием в качестве смещения. Все, что делает компилятор, — это преобразует вычисленное вручную линейное смещение в причудливый синтаксис, который легче читать.





Используйте массив указателей.
#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]);
}
Попробуйте показать код, необходимый для освобождения выделенной памяти.
Дело в том, что изначально я получил массив (а не массив указателей), с чем бы мне не хотелось возиться.
Хорошо, я освободил память.
Вы можете просто использовать 2d-массив и затем элементы, просто array[m][n] и использовать цикл for для помещения элементов в массив. malloc не нужен
В то время как хороший общий подход, как правило, по моему ограниченному опыту, два уровня косвенности, как правило, немного медленнее, чем вычисление линейного индекса. И у вас не должно быть проблем с синтаксисом в свете этого комментария: stackoverflow.com/questions/54282732/…
Вы не можете объявить глобальные массивы в 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 — нет.Я думаю, что ключевое слово extern предотвращает выделение переменной памяти в стеке. Кроме того, он не был бы в статической зоне, если бы он был внутри функционального блока.
Можно утверждать, что int *array = malloc(…); не может быть глобальной переменной, потому что в C вы не можете использовать результат вызова функции для инициализации глобальной (или статической) переменной.
После:
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] и т. д. Его можно передавать в функции. Я не показывал это здесь, но также тривиально поместить код инициализации в функцию, а затем иметь несколько разных размеров матрицы в одной функции.
Да ладно, это C, наведите указатель на то, что вы хотите