3
p = (int *)malloc(m * n * sizeof(int));

If I use p as a two-dimensional dynamic array, how do I access the elements inside?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
chess
  • 45
  • 3
  • 1
    Does the array have m rows with n values in each row, or does it have n rows with m values in each row? – Jonathan Leffler May 29 '22 at 14:19
  • You should compute the size as `sizeof(int) * m * n` to ensure the multiplication `n * m` is performed using `size_t` arithmetics, not `int` that might overflow if `n` and `m` are defined as `int`. – chqrlie May 29 '22 at 16:39

5 Answers5

4

If you can rely on your C implementation to support variable-length arrays (an optional feature), then a pretty good way would be to declare p as a pointer to (variable-length) array instead of a pointer to int:

int (*p)[n] = malloc(m * sizeof(*p));  // m rows, n columns

Then you access elements using ordinary double indexes, just as if you had declared an ordinary 2D array:

p[0][0] = 1;
p[m-1][n-1] = 42;
int q = p[2][1];

Most widely used C implementations do support VLAs, but Microsoft's is a notable exception.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I think that most libraries dealing with multi dimensional arrays (e.g. opencv [c++], and numpy [python]), uses the approach of allocating one block of memory, and accessing it "manually" via the indices and strides. It offer advantages like support padding, and an arbitrary number of dimensions. What do you think of this approach vs what you suggested in your answer ? – wohlstad May 29 '22 at 15:05
  • Pointers to VLA will become mandatory feature in C23, while only automatic VLAs will stay optional – tstanisl May 29 '22 at 17:15
  • @wohlstad, the VLA approach described here allocates all the memory in one block, and accesses it with conventional array-access syntax. It can support any number of dimensions. The offset calculations are exactly the same that you might write manually, but you don't have to write them manually. You can slice them or use other kinds of access patterns exactly as you would with a static or automatic multi-dimensional array. The pointer's type is the same one that a corresponding (declared) array would decay to. – John Bollinger May 30 '22 at 12:27
  • So I've heard, @tstanisl, and I think that's terrific. It covers a large majority of my actual VLA use cases. But right now, in the current standard, all aspects of VLAs are optional, and that's what I'm going to be saying until C23 is published with those changes. – John Bollinger May 30 '22 at 12:32
  • moreover the compiler can assume that `p[i][j]` and `p[x][y]` never alias except the case if `i==x && j == y`. In theory it should encourage optimization like vectorization. This is not possible for manual indexing without deeper loop analysis. – tstanisl Jun 30 '22 at 14:08
3

I'd personally prefer using wohlstad's method, but you could also variably-modified types, which are an optional feature of C11, but will probably be mandated in C2x:

int (*p)[m] = malloc(n * sizeof *p);

This can now be used just like a normal 2d array with automatic storage duration.

int n = 12, m = 9;

int (*p)[m] = malloc(n * sizeof *p);
for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j)
        p[i][j] = i * m + j;
    
printf("%d\n", p[4][2]);

free(p);
camel-cdr
  • 643
  • 4
  • 8
2

I'll assume m is the number of columns, and n the number of rows (you can use n instead of m in my answer if it's the opposite).
In order to access the 2D array, you need 2 indices - let's call them x and y:
x index will be in the range 0 .. m-1, and
y index will be in the range 0 .. n-1

You can calculate the index for your p array in the following way:

int p_idx = y * m + x

Then you can access your arrays element e.g. this way:

p[p_idx] = 111;   // set an element value
int a = p[p_idx]; // get an element value
wohlstad
  • 12,661
  • 10
  • 26
  • 39
0

You can't use p as a two-dimensional array. It's a single integer pointer. Two-dimensional (dynamically allocated) implies nested pointers. However, you can represent a two-dimensional array in a "flattened" form. Here's some code that might offer a helpful explanation:

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

int main(){
    // Populating a 10x5 matrix
    int m = 10;
    int n = 5;
    int* p = (int*) malloc(m*n*sizeof(int));
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            // Each row has n elements; to get the
            // "flattened" index, treating the MxN
            // matrix as row-major ordered (reading
            // left-to-right, and THEN down the rows):
            int flattened_index = (i * n) + j;

            // E.g., populate with multiplication table data
            p[flattened_index] = (i + 1) * (j + 1);

            printf("%d\t", p[flattened_index]);
        }
        printf("\n");
    }

    // Inversely, to convert a flattened index to a
    // row and column, you have to use modulus
    // arithmetic
    int flattened_index = 21;
    int row = flattened_index / n; // Rounded-down integer division
    int column = flattened_index % n; // Remainder after division
    
    printf("%d * %d = %d\n", row + 1, column + 1, p[flattened_index]);

    return 0;
}

This outputs:

1   2   3   4   5   
2   4   6   8   10  
3   6   9   12  15  
4   8   12  16  20  
5   10  15  20  25  
6   12  18  24  30  
7   14  21  28  35  
8   16  24  32  40  
9   18  27  36  45  
10  20  30  40  50  
5 * 2 = 10
Alexander Guyer
  • 2,063
  • 1
  • 14
  • 20
0

You are actually creating a single-dimensional array. But still, we can use it to hold a matrix considering the fact in C a multidimensional array, e.g int mat[m][n], is stored in contiguous memory itself.

#include <iostream>

int main()
{
   int m, n;

   std::cin >> m >> n;

   int* mat_ptr = (int*)malloc(m * n * sizeof(int));

   if (mat_ptr)
   {
      for (int row = 0; row < m; ++row)
      {
        for (int col = 0; col < n; ++col)
        {
            *(mat_ptr + ((row * n) + col)) = (row * n) + col;
        }
      }

      for (int row = 0; row < m; ++row)
      {
        for (int col = 0; col < n; ++col)
        {
            std::cout << *(mat_ptr + ((row * n) + col)) << " ";
        }

        std::cout << std::endl;
      }
  }

  return 0;
}
Nitheesh George
  • 1,357
  • 10
  • 13