malloc in C, but use multi-dimensional array synta

2019-01-13 11:17发布

Is there any way to malloc a large array, but refer to it with 2D syntax? I want something like:

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


UPDATE: This was important to mention: I just want to have one contiguous block of memory. I just don't want to write a macro like:

#define INDX(a,b) (a*200+b);

and then refer to my blob like:

memory[INDX(a,b)];

I'd much prefer:

memory[a][b];


UPDATE: I understand the compiler has no way of knowing as-is. I'd be willing to supply extra information, something like:

int *MAGICVAR[][200] = memory;

Does no syntax like this exist? Note the reason I don't just use a fixed width array is that it is too big to place on the stack.


UPDATE: OK guys, I can do this:

void toldyou(char MAGICVAR[][286][5]) {
  //use MAGICVAR
}

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

I get a warning, passing arg 1 of toldyou from incompatible pointer type, but the code works, and I've verified that the same locations are accessed. Is there any way to do this without using another function?

8条回答
老娘就宠你
2楼-- · 2019-01-13 11:55

If extra indirection isn't a concern, you can use an array of pointers.

Edit

Here's a variation on @Platinum Azure's answer that doesn't make so many calls to malloc. Besides faster allocation, all the elements are guaranteed to be contiguous:

#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;
查看更多
趁早两清
3楼-- · 2019-01-13 11:55
int** memory = malloc(sizeof(*memory)*400); 
for (int i=0 ; i < 400 ; i++) 
{
    memory[i] = malloc(sizeof(int)*200);
}
查看更多
何必那么认真
4楼-- · 2019-01-13 11:56

Use a pointer to arrays:

#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;
}
查看更多
Emotional °昔
5楼-- · 2019-01-13 12:00
#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:

Arrays and pointers look very much the same, but the compiler treats them very differently. Let's see what needs to be done for an array indexing and de-referencing a pointer with offset:

  1. Say we declared a static array (array on the stack is just a bit more complicated, fixed offset from a register, but essentially the same):

    static int array[10];

  2. And a pointer:

    static int* pointer;

  3. We then de-deference each as follows:

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

The thing to note is that address of the beginning of array, as well as, address of pointer (not its contents) are fixed at link/load time. Compiler then does the following:

  1. For array de-reference:
    • loads value of i,
    • adds it to the value of array, i.e. its fixed address, to form target memory address,
    • loads the value from calculated address
  2. For pointer de-reference:
    • loads the value of i,
    • loads the value of pointer, i.e. the contents at its address,
    • adds two values to form the effective address
    • loads the value from calculated address.

Same happens for 2D array with additional steps of loading the second index and multiplying it by the row size (which is a constant). All this is decided at compile time, and there's no way of substituting one for the other at run-time.

Edit:

@caf here has the right solution. There's a legal way within the language to index a pointer as two-dimentional array after all.

查看更多
贼婆χ
6楼-- · 2019-01-13 12:11

The compiler and runtime have no way of knowing your intended dimension capacities with only a multiplication in the malloc call.

You need to use a double pointer in order to achieve the capability of two indices. Something like this should do it:

#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;

Make sure you check all your malloc return values for NULL returns, indicating memory allocation failure.

查看更多
ゆ 、 Hurt°
7楼-- · 2019-01-13 12:13

Yes, you can do this, and no, you don't need another array of pointers like most of the other answers are telling you. The invocation you want is just:

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

If you wish to declare a function returning such a pointer, you can either do it like this:

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

    return MAGICVAR;
}

Or use a typedef, which makes it a bit clearer:

typedef int (*arrayptr)[200];

arrayptr function(void)
{
    /* ... */
查看更多
登录 后发表回答