Использование malloc для выделения многомерных массивов с разными длинами строк
У меня есть следующее C
код:
int *a;
size_t size = 2000*sizeof(int);
a = (int *) malloc(size);
который отлично работает Но если у меня есть следующее:
char **b = malloc(2000*sizeof *b);
где каждый элемент b
имеет разную длину.
Как можно сделать то же самое для b
как я сделал для a
; то есть следующий код будет правильным?
char *c;
size_t size = 2000*sizeof(char *);
c = (char *) malloc(size);
8 ответов
Во-первых, вам нужно выделить массив указателей, таких как char **c = malloc( N * sizeof( char* ))
затем выделите каждую строку отдельным вызовом malloc
, вероятно, в цикле:
/* N is the number of rows */
/* note: c is char** */
if (( c = malloc( N*sizeof( char* ))) == NULL )
{ /* error */ }
for ( i = 0; i < N; i++ )
{
/* x_i here is the size of given row, no need to
* multiply by sizeof( char ), it's always 1
*/
if (( c[i] = malloc( x_i )) == NULL )
{ /* error */ }
/* probably init the row here */
}
/* access matrix elements: c[i] give you a pointer
* to the row array, c[i][j] indexes an element
*/
c[i][j] = 'a';
Если вы знаете общее количество элементов (например, N*M
) Вы можете сделать это в одном выделении.
Типичная форма для динамического выделения массива NxM типа T
T **a = malloc(sizeof *a * N);
if (a)
{
for (i = 0; i < N; i++)
{
a[i] = malloc(sizeof *a[i] * M);
}
}
Если каждый элемент массива имеет разную длину, замените M соответствующей длиной для этого элемента; например
T **a = malloc(sizeof *a * N);
if (a)
{
for (i = 0; i < N; i++)
{
a[i] = malloc(sizeof *a[i] * length_for_this_element);
}
}
Эквивалентное распределение памяти для char a[10][20]
будет следующим.
char **a;
a=(char **) malloc(10*sizeof(char *));
for(i=0;i<10;i++)
a[i]=(char *) malloc(20*sizeof(char));
Я надеюсь, что это выглядит просто для понимания.
Другой подход состоял бы в том, чтобы выделить один непрерывный кусок памяти, содержащий блок заголовка для указателей на строки, а также блок тела для хранения фактических данных в строках. Затем просто разметьте память, назначив адреса памяти в теле указателям в заголовке для каждой строки. Это будет выглядеть следующим образом:
int** 2dAlloc(int rows, int* columns) {
int header = rows * sizeof(int*);
int body = 0;
for(int i=0; i<rows; body+=columnSizes[i++]) {
}
body*=sizeof(int);
int** rowptr = (int**)malloc(header + body);
int* buf = (int*)(rowptr + rows);
rowptr[0] = buf;
int k;
for(k = 1; k < rows; ++k) {
rowptr[k] = rowptr[k-1] + columns[k-1];
}
return rowptr;
}
int main() {
// specifying column amount on per-row basis
int columns[] = {1,2,3};
int rows = sizeof(columns)/sizeof(int);
int** matrix = 2dAlloc(rows, &columns);
// using allocated array
for(int i = 0; i<rows; ++i) {
for(int j = 0; j<columns[i]; ++j) {
cout<<matrix[i][j]<<", ";
}
cout<<endl;
}
// now it is time to get rid of allocated
// memory in only one call to "free"
free matrix;
}
Преимущество этого подхода - элегантное освобождение памяти и возможность использовать массивоподобную нотацию для доступа к элементам результирующего двумерного массива.
Если каждый элемент в b имеет разную длину, вам нужно сделать что-то вроде:
int totalLength = 0;
for_every_element_in_b {
totalLength += length_of_this_b_in_bytes;
}
return (char **)malloc(totalLength);
Я думаю, что двухэтапный подход является лучшим, потому что c 2-d массивы - это просто и массив массивов. Первым шагом является выделение одного массива, а затем цикл по нему с выделением массивов для каждого столбца. Эта статья дает хорошие детали.
2-D массив динамического распределения памяти
int **a,i;
// for any number of rows & columns this will work
a = (int **)malloc(rows*sizeof(int *));
for(i=0;i<rows;i++)
*(a+i) = (int *)malloc(cols*sizeof(int));
malloc не размещает на определенных границах, поэтому следует предположить, что он размещает на границе байтов.
Возвращенный указатель затем не может быть использован при преобразовании в любой другой тип, поскольку доступ к этому указателю, вероятно, вызовет нарушение доступа к памяти со стороны ЦП, и приложение будет немедленно закрыто.