malloc в C, но использовать синтаксис многомерного массива - PullRequest
13 голосов
/ 29 июня 2010

Можно ли как-то использовать malloc для большого массива, но ссылаться на него с помощью 2D-синтаксиса? Я хочу что-то вроде:

int *memory = (int *)malloc(sizeof(int)*400*200);
int MAGICVAR = ...;
MAGICVAR[20][10] = 3; //sets the (200*20 + 10)th element


ОБНОВЛЕНИЕ: это было важно упомянуть: я просто хочу иметь один непрерывный блок памяти. Я просто не хочу писать макрос вроде:
#define INDX(a,b) (a*200+b);

, а затем сослаться на мой блоб как:

memory[INDX(a,b)];

Я бы предпочел:

memory[a][b];


ОБНОВЛЕНИЕ: я понимаю, что у компилятора нет способа узнать как есть. Я хотел бы предоставить дополнительную информацию, что-то вроде:
int *MAGICVAR[][200] = memory;

Не существует такого синтаксиса? Обратите внимание, что я не просто использую массив фиксированной ширины, потому что он слишком большой для размещения в стеке.


ОБНОВЛЕНИЕ: ОК, ребята, я могу сделать это:
void toldyou(char MAGICVAR[][286][5]) {
  //use MAGICVAR
}

//from another function:
  char *memory = (char *)malloc(sizeof(char)*1820*286*5);
  fool(memory);

Я получаю предупреждение, passing arg 1 of toldyou from incompatible pointer type, но код работает, и я проверил, что к тем же адресам обращаются. Есть ли способ сделать это без использования другой функции?

Ответы [ 8 ]

27 голосов
/ 30 июня 2010

Да, вы можете сделать это, и нет, вам не нужен другой массив указателей, как большинство других ответов говорят вам.Требуемый вызов:

int (*MAGICVAR)[200] = malloc(400 * sizeof *MAGICVAR);
MAGICVAR[20][10] = 3; // sets the (200*20 + 10)th element

Если вы хотите объявить функцию, возвращающую такой указатель, вы можете сделать это следующим образом:

int (*func(void))[200]
{
    int (*MAGICVAR)[200] = malloc(400 * sizeof *MAGICVAR);
    MAGICVAR[20][10] = 3;

    return MAGICVAR;
}

Или использоватьtypedef, что делает его немного понятнее:

typedef int (*arrayptr)[200];

arrayptr function(void)
{
    /* ... */
19 голосов
/ 30 июня 2010

Использовать указатель на массивы:

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

int main()
{
    int (*arr)[10];

    arr = malloc(10*10*sizeof(int));
    for (int i = 0; i < 10; i++)
        for(int j = 0; j < 10; j++)
            arr[i][j] = i*j;

    for (int i = 0; i < 10; i++)
        for(int j = 0; j < 10; j++)
            printf("%d\n", arr[i][j]);
    free(arr);
    return 0;
}
4 голосов
/ 29 июня 2010

Если дополнительная косвенность не имеет значения, вы можете использовать массив указателей.

Редактировать

Вот вариант ответа @Platinum Azure, в котором не так много вызовов malloc. Помимо более быстрого выделения, все элементы гарантированно будут смежными:

#define ROWS 400
#define COLS 200

int **memory = malloc(ROWS * sizeof(*memory));
int *arr = malloc(ROWS * COLS * sizeof(int));

int i;
for (i = 0; i < ROWS; ++i)
{
    memory[i] = &arr[i * COLS];
}

memory[20][10] = 3;
2 голосов
/ 30 июня 2010

В том же ключе, что и в ответе Когвил, вот (несколько грязный) трюк, который делает только один вызов malloc():

#define ROWS 400
#define COLS 200
int** array = malloc(ROWS * sizeof(int*) + ROWS * COLS * sizeof(int));
int i;
for (i = 0; i < ROWS; ++i)
    array[i] = (int*)(array + ROWS) + (i * COLS);

Это заполняет первую часть буфера указателями на каждую строкув следующих непосредственно смежных массивах данных.

2 голосов
/ 29 июня 2010
#define ROWS 400
#define index_array_2d(a,i,j) (a)[(i)*ROWS + (j)]
...
index_array_2d( memory, 20, 10 ) = -1;
int x = index_array_2d( memory, 20, 10 );

Edit:

Массивы и указатели выглядят почти одинаково, но компилятор обрабатывает их совершенно по-разному. Давайте посмотрим, что нужно сделать для индексации массива и разыменования указателя со смещением:

  1. Скажем, мы объявили статический массив (массив в стеке немного сложнее, фиксированное смещение от регистра, но по сути то же самое):

    static int array[10];

  2. И указатель:

    static int* pointer;

  3. Затем мы разыменовываем каждый следующим образом:

    x = array[i];
    x = pointer[i];

Следует отметить, что адрес начала array, а также адрес pointer (не его содержимое) фиксированы во время соединения / загрузки. Затем компилятор делает следующее:

  1. Для array разыменование:
    • загружает значение i,
    • добавляет его к значению array, то есть к его фиксированному адресу, чтобы сформировать целевой адрес памяти,
    • загружает значение из рассчитанного адреса
  2. Для pointer разыменование:
    • загружает значение i,
    • загружает значение pointer, то есть содержимое по его адресу,
    • добавляет два значения для формирования эффективного адреса
    • загружает значение из рассчитанного адреса.

То же самое происходит для двумерного массива с дополнительными шагами загрузки второго индекса и умножения его на размер строки (который является константой). Все это решается во время компиляции, и нет никакого способа заменить одно во время выполнения.

Edit:

@ caf здесь есть правильное решение. В языке есть законный способ индексировать указатель как двумерный массив.

1 голос
/ 29 июня 2010

Компилятор и среда выполнения не имеют возможности узнать ваши предполагаемые емкости измерений с помощью только умножения в вызове malloc.

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

#define ROWS 400
#define COLS 200

int **memory = malloc(ROWS * sizeof(*memory));

int i;
for (i = 0; i < ROWS; ++i)
{
    memory[i] = malloc(COLS * sizeof(*memory[i]);
}

memory[20][10] = 3;

Убедитесь, что вы проверили все возвращаемые значения malloc на NULL, что указывает на ошибку выделения памяти.

0 голосов
/ 12 февраля 2015

Опираясь на ответы Тима и Кафа, я оставлю это здесь для потомков:

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

void Test0() {
    int                             c, i, j, n, r;
    int                             (*m)[ 3 ];

    r = 2;
    c = 3;

    m = malloc( r * c * sizeof(int) );

    for ( i = n = 0; i < r; ++i ) {
        for ( j = 0; j < c; ++j ) {
            m[ i ][ j ] = n++;
            printf( "m[ %d ][ %d ] == %d\n", i, j, m[ i ][ j ] );
        }
    }

    free( m );
}

void Test1( int r, int c ) {
    int                             i, j, n;

    int                             (*m)[ c ];

    m = malloc( r * c * sizeof(int) );

    for ( i = n = 0; i < r; ++i ) {
        for ( j = 0; j < c; ++j ) {
            m[ i ][ j ] = n++;
            printf( "m[ %d ][ %d ] == %d\n", i, j, m[ i ][ j ] );
        }
    }

    free( m );
}

void Test2( int r, int c ) {
    int                             i, j, n;

    typedef struct _M {
        int                         rows;
        int                         cols;

        int                         (*matrix)[ 0 ];
    } M;

    M *                             m;

    m = malloc( sizeof(M) + r * c * sizeof(int) );

    m->rows = r;
    m->cols = c;

    int                             (*mp)[ m->cols ] = (int (*)[ m->cols ]) &m->matrix;

    for ( i = n = 0; i < r; ++i ) {
        for ( j = 0; j < c; ++j ) {
            mp[ i ][ j ] = n++;
            printf( "m->matrix[ %d ][ %d ] == %d\n", i, j, mp[ i ][ j ] );
        }
    }

    free( m );
}

int main( int argc, const char * argv[] ) {
    int                             cols, rows;

    rows = 2;
    cols = 3;

    Test0();
    Test1( rows, cols );
    Test2( rows, cols );

    return 0;
}
0 голосов
/ 29 июня 2010
int** memory = malloc(sizeof(*memory)*400); 
for (int i=0 ; i < 400 ; i++) 
{
    memory[i] = malloc(sizeof(int)*200);
}
...