19

Every time I allocate the memory for a 2D array first I create an array of int** and then with a for I allocate the memory for each element.

For example:

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

Wouldn't be possible allocate the memory like:

int ** arr = malloc(N*sizeof(int[M]));

or

int ** arr = malloc(sizeof(int[N][M]));

in order to avoid the for?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
ninazzo
  • 547
  • 1
  • 6
  • 17

3 Answers3

30

like this : int (*arr)[M] = malloc(sizeof(int[N][M]));

arr is pointer to int[M].

use like arr[0][M-1];

and free(arr);

BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • 5
    Even better - `int (*arr)[M] = malloc( sizeof *arr * N );`. This way you don't have to modify the `sizeof` expression if the type of `arr` ever changes. – John Bode Apr 27 '16 at 14:19
  • @JohnBode see [this answer](http://stackoverflow.com/a/32985668/971127) – BLUEPIXY Apr 27 '16 at 14:33
  • Obviously that's only an issue if `M` isn't a compile time constant. And even if it is, "evaluate" doesn't have to be synonymous with "dereference". All that needs to be "evaluated" is the *type* of the expression `*arr`; there's no logical reason that `sizeof` would ever need to *dereference* `arr` to determine its type. Yes, if you want to be strictly pendantic (and who among us doesn't), the behavior is undefined, but only because the definition of "evaluate" is imprecise. – John Bode Apr 27 '16 at 16:21
  • 1
    @JohnBode In that post I am a similar opinion with you. However, it should be avoided because it does not find no guarantee of such operations in the standard. – BLUEPIXY Apr 27 '16 at 16:27
  • 3
    And then there's this language from 5.1.2.3/4: "In the abstract machine, all expressions are evaluated as specified by the semantics. **An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced** (including any caused by calling a function or accessing a volatile object)." I think this gives us an out for using `sizeof` on a pointer to a VLA. – John Bode Apr 27 '16 at 16:35
  • @JohnBode I think that very has been matched to our claim (and behavior of gcc). I will remember this. Do you can try to point out to that answer and the post? – BLUEPIXY Apr 27 '16 at 16:44
  • 1
    @BLUEPIXY So this doesn't work when M is a variable? – Anchith Acharya Mar 28 '20 at 11:41
24

int ** arr = malloc(N*sizeof(int[M]));

is incorrect C code, if you simulate it by allocating once
int *arr = malloc(N*M*sizeof(int));
and access it by
arr[i*M + j],
this is an analog to arr[I][j] in your first case.

Gerhard
  • 6,850
  • 8
  • 51
  • 81
fluter
  • 13,238
  • 8
  • 62
  • 100
  • 6
    @Olaf Calm down, there is no 2D array you can have with C, the OP want is to allocate once for a buffer to hold NxM ints. – fluter Apr 27 '16 at 13:06
  • 2
    Plus, it is not at all "obsolete", it is something you see today. It all depends your use case. It's odd you can declare some way of code as "obsolete" after all. – fluter Apr 27 '16 at 13:07
  • 1
    I just asked if there was a way **like** that, I never said that **that** was the way. – ninazzo Apr 27 '16 at 13:11
  • 2
    @fluter: You very well can! See my answer. And yes, it _is_ obsolete. It is just that there are too many programmers who think there are no multi-dimensional arrays in C. But just beause "we did it that way all the time" does not mean it is the correct way. In fact, there is not a single advantage in your version, but a lot against it, e.g.: error-prone, harder to understand, you have to trace the inner-length yourself. – too honest for this site Apr 27 '16 at 13:15
  • Oh, and sorry, for the hefty reaction. It is just too much confusion about arrays and pointers. And too many programmers in fear of using complex arrays, although it becomes pretty simple once you understood the concept (things get worse with qualifiers, though). – too honest for this site Apr 27 '16 at 13:22
  • @Olaf I agree with you that use a pointer to an array is a good one, and I didn't say the way I used is the only way, it's just one solution. Again, it's a pointer to an array, not a 2D array by nature. – fluter Apr 27 '16 at 13:28
  • To recover the ability to use `arr[i][j]` syntax, folow up with casting to `int (*)[M]`. Do `int (*arr)[M] = int (*)[M] malloc(N*M*sizeof(int))`. – rfabbri Apr 29 '19 at 21:39
11

You have a "pointer to pointer". That cannot represent a 2D array.

The correct declaration of a pointer to a 2D array is

// number of elements in one row
#define COLS 10

// number of rows
#define ROWS 20

int (*array)[COLS];   // mind the parenthesis!

That makes array a pointer to array of COLS ints. The type is `int (*)[COLS], btw. but you don't need the type, see below.

To allocate the array you should then use the standard allocation for a 1D array:

array = malloc(sizeof(*array) * ROWS);   // COLS is in the `sizeof`

array = malloc(sizeof(int[ROWS][COLS])); // explicit 2D array notation

Which variant to use is personal style. While the first contains no redundancy (consider you change the declaration of array to use INNER instead of COLS or the element-type to float). The second is more clear at a fist glance, but more prone to errors when modifying the declaration of array.

To free:

free(array);
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • 3
    I'd be curious to know what the downvoter's objection was. – Michael Petch Apr 28 '16 at 13:16
  • 2
    @MichaelPetch: Me too. But you cannot argue with <(well, you might have an idea what I would write here)>. – too honest for this site Apr 28 '16 at 13:19
  • 1
    `array = int (*)[COLS] malloc(ROWS*COLS*sizeof(int))` _may_ be more intutive – rfabbri Apr 29 '19 at 21:35
  • 1
    @rfabbri That's less intuitive than the second variant I propose. Additionally the first proposed version also guarantees the inner index to be always consistent to the pointer type. This is not true for the second and your variant. Finally: never typecast ´void *´ in C! This is one of the stupidities of some coding styles. Please note, this is different in C++, but that's a different language not subject here. – too honest for this site Mar 06 '21 at 16:20