defining a 2D array with malloc and modifying it

10×30:

int(*array)[30] = malloc((sizeof *array) * 10);

15×20:

int(*array)[20] = malloc((sizeof *array) * 15);

Resizing to 20×25:

int(*array2)[25] = realloc(array, (sizeof *array2) * 20);

The outer dimension (10, 15, 20) can be determined at runtime, because it’s not need as part of index calculations by the compiler. The inner dimension (30, 20, 25) needs to be known at compile time. Hope it helps.

Note that unlike the array-of-pointer solutions, this one can be handled as a single block of memory, because it allocates everything in a single chunk of memory like a real declared array:

memcpy(somewhere, array2, sizeof(int) * 20 * 25); // (sizeof *array2) * 20

It ultimately depends on your usecase, though.


Since some people have difficulties understanding the actions taken by an index operation to array, let’s what Clang gives us for an index expression in the following code

int main() {
  int(*array)[10] = malloc((sizeof *array) * 5);
  array[4][9] = 0;

  int(*array1)[10][5] = malloc((sizeof *array1) * 20);
  array1[19][9][4] = 0;
}

It’s a nice compiler, which can print its AST in an easily readable manner

  // array[4][9] = 0;
  (BinaryOperator 0xba62cc0 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xba62c80 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xba62c60 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba62c20 <col:3, col:10> 'int [10]'
          (DeclRefExpr 0xba62bdc <col:3> 'int (*)[10]' Var="array" 0xba62a00)
          (IntegerLiteral 0xba62c00 <col:9> 'int' 4)))
      (IntegerLiteral 0xba62c40 <col:12> 'int' 9))
    (IntegerLiteral 0xba62ca0 <col:17> 'int' 0))

  // array1[19][9][4] = 0;
  (BinaryOperator 0xba630b8 <line:8:3, col:22> 'int' '='
    (ArraySubscriptExpr 0xba63078 <col:3, col:18> 'int'
      (ImplicitCastExpr 0xba63058 <col:3, col:15> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba63018 <col:3, col:15> 'int [5]'
          (ImplicitCastExpr 0xba62ff8 <col:3, col:12> 'int (*)[5]' <ArrayToPointerDecay>
            (ArraySubscriptExpr 0xba62fa0 <col:3, col:12> 'int [10][5]'
              (DeclRefExpr 0xba62f5c <col:3> 'int (*)[10][5]' Var="array1" 0xba62db0)
              (IntegerLiteral 0xba62f80 <col:10> 'int' 19)))
          (IntegerLiteral 0xba62fc0 <col:14> 'int' 9)))
      (IntegerLiteral 0xba63038 <col:17> 'int' 4))
    (IntegerLiteral 0xba63098 <col:22> 'int' 0)))

Note that each array subscript expression takes a pointer, adds the value of the index, and yields the addressed element. If that subelement is an array, it is decayed to a pointer to its first element. This is really not actually different than the steps done for a declared array

int main() {
  int array[5][10] = { };
  array[4][9] = 1;
}

Yields a very similar AST, with just the most inner expression first being decayed to a pointer to its first element

  // array[4][9] = 1;
  (BinaryOperator 0xbf9f7e8 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xbf9f7a8 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xbf9f788 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xbf9f748 <col:3, col:10> 'int [10]'
          (ImplicitCastExpr 0xbf9f728 <col:3> 'int (*)[10]' <ArrayToPointerDecay>
            (DeclRefExpr 0xbf9f6cc <col:3> 'int [5][10]' Var="array" 0xbfa81f0))
          (IntegerLiteral 0xbf9f6f0 <col:9> 'int' 4)))
      (IntegerLiteral 0xbf9f768 <col:12> 'int' 9))
    (IntegerLiteral 0xbf9f7c8 <col:17> 'int' 1)))

Leave a Comment