2

It might be a stupid question, but I wonder if there is a efficient way to do this.

The situation:

int* array = malloc(n * m * sizeof(int));
//want to convert array into M[n][m]

what I am doing now:

int** M = malloc(n * sizeof(int*));
for(int i = 0; i < n; i++, array += m)
  M[i] = array; 

I don't think the conversion should be this complex. Is there any simple syntax C provided? Can I declare an extern M[n][m] then set its address to the array?

(error handling and memory management in the sample is omitted for simplicity. Just think it as a part of some function.)

Yi Lin Liu
  • 169
  • 1
  • 11
  • 3
    C'mon man this is C, cast the pointer to what you want – smac89 Jan 21 '19 at 02:14
  • @smac89 I want to use the syntax of index M[a][b] for some other reason. – Yi Lin Liu Jan 21 '19 at 02:15
  • @smac89 can I cast it to a int[n][m] type? – Yi Lin Liu Jan 21 '19 at 02:17
  • 1
    Seems to work here: http://coliru.stacked-crooked.com/a/6790414229295052 – smac89 Jan 21 '19 at 02:32
  • @smax89 wow, that is much more dynamic than I imagined. How is the size recognized during runtime? That is amazing. – Yi Lin Liu Jan 21 '19 at 02:45
  • @YiLinLiu. It's not. You pass it in. 2D arrays have to have the first dimensions specified because the compiler computes the linear offset for you. It's at runtime in the sense that the variables you use are dynamic, but the offset expression is computed at compile time. – Mad Physicist Jan 21 '19 at 03:09
  • @MadPhysicist do you mean the compiler traced its value? What if the functions are extremely complicate that the compiler cannot tell? – Yi Lin Liu Jan 21 '19 at 03:12
  • 1
    @MadPhysicist, I was just about to comment the same as above. How is it computed in this case: http://coliru.stacked-crooked.com/a/39e6761edaccd062? Here the size is not known until runtime – smac89 Jan 21 '19 at 03:13
  • 2
    @smac89 in the same way that you'd compute `i + sp * j` for any other variables. When you say `int (*p)[sp]`, that means for a given `i`, `j`, multiply the `j` by `sp` before adding to `i` and using as an offset. All the compiler is doing is converting the manually computed linear offset into a fancy syntax that's easier to read. – Mad Physicist Jan 21 '19 at 03:21

4 Answers4

3

After:

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

you can do:

int (*M)[m] = (int(*)[m])array;

and then use M[1][2] for example.

You could have done that in the first place too :

int (*M)[m] = malloc( n * sizeof *M );
M.M
  • 138,810
  • 21
  • 208
  • 365
1

You can't declare global arrays in C without giving them a specific numerical size. This is because global variables are static and the compiler can't allocate a variable amount of memory for a global array.

In C you've got to remember that an array is actually just a pointer. When you're asking for int *array = malloc(n * sizeof(int)) what you're telling the compiler is that you need n lots of 4 byte blocks of int type reserved side by side in memory, where the value of array is actually a pointer to the first 4 byte block.

When you are accessing elements of an array you are actually doing pointer arithmetic and dereferencing the pointer, but this is hidden in the array[i] syntax. So, when array has int type, array[2] translates as go to the location given by the array pointer (i.e. the head) now move 2 * 4 bytes along in memory and dereference the pointer to access the integer stored there.

So when you're creating a 2-d array as you've discussed, there really isn't a better way of doing it. Make sure you have a firm grip on what it actually is you're getting from the compiler. Pointers are (on 64-bit machines anyway) 8 bytes and ints are 4 bytes. So when you call int **M = malloc(sizeof(int*) * m the compiler allocates you m blocks of width 8 bytes each, all of which have type int*.

From other programming languages it seems very over the top having to declare a pointer reference to a block of pointers, but getting passed the higher level idea of an array and considering them as a collection of pointers will really help you in the long run. When you need to pass these data types between functions you need to be able to have a firm idea of what you are actually manipulating; a pointer, a value, a pointer to a pointer? It will help you a lot in debugging code these ideas, as it is very easy to try and perform computations on pointers rather than values.

3 Useful Tips:

  • calloc(n, sizeof(int)) might be a better fit than calling malloc because calloc automatically initialises your entries to zero whereas malloc doesn't.
  • when calling calloc/malloc, you want to check that your dynamic memory allocation has been successful; if it isn't successful, then malloc will return NULL.
  • A good rule of thumb is that every time you call malloc you want to call free once you're done with the memory. This can help to prevent memory leaks.
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Joel Biffin
  • 348
  • 1
  • 6
  • 18
  • 1
    I think the extern keyword prevent the variable allocating memory on stack. Also, it would not be in the static zone if I have it inside a function block. – Yi Lin Liu Jan 21 '19 at 02:57
  • 1
    It's feasible to argue that the `int *array = malloc(…);` cannot be a global variable because in C you cannot use the result of a function call to initialize a global (or static) variable. – Jonathan Leffler Jan 21 '19 at 03:05
1

The tricky part is declaring the variable to hold the pointer to the allocated array; the rest is straight-forward — assuming you have a C99 or later compiler.

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

static void print_2dvla(int rows, int cols, int data[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        printf("%2d: ", i);
        for (int j = 0; j < cols; j++)
            printf(" %4d", data[i][j]);
        putchar('\n');
    }
}

int main(void)
{
    int m = 10;
    int n = 12;

    int (*M)[m] = malloc(n * m * sizeof(M[0][0]));
    if (M == NULL)
    {
        fprintf(stderr, "Failed to allocate %zu bytes memory\n", n * m * sizeof(M[0][0]));
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
            M[i][j] = (i + 1) * 100 + (j + 1);
    }

    print_2dvla(n, m, M);

    free(M);
    return 0;
}

Example output:

 0:   101  102  103  104  105  106  107  108  109  110
 1:   201  202  203  204  205  206  207  208  209  210
 2:   301  302  303  304  305  306  307  308  309  310
 3:   401  402  403  404  405  406  407  408  409  410
 4:   501  502  503  504  505  506  507  508  509  510
 5:   601  602  603  604  605  606  607  608  609  610
 6:   701  702  703  704  705  706  707  708  709  710
 7:   801  802  803  804  805  806  807  808  809  810
 8:   901  902  903  904  905  906  907  908  909  910
 9:  1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
10:  1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
11:  1201 1202 1203 1204 1205 1206 1207 1208 1209 1210

The key line is:

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

This says that M is a pointer to an array of int arrays each of which has the dimension m. The rest of the code simply uses that array with the usual 2-subscript notation — M[i][j] etc. It can be passed to functions. I've not shown it here, but it is trivial to put the initialization code into a function too, and then have several different sizes of matrix in a single function.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Use an array of pointers.

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

int main() 
{ 
    int n = 3, m = 4, i, j, count=0;

    int *array[n];
    for(i=0; i<n; i++)
     array[i] = (int *)malloc(m * sizeof(int));
     if( array[i] == NULL)
     {
        perror("Unable to allocate array");
        exit(1);
     }

    // going to add number to your 2d array.

    for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         array[i][j] = ++count;

    for (i=0; i < n; i++)
      for (j=0; j < m; j++)
         printf("%d ", array[i][j]);

      // free memory

      for(i=0; i<n; i++)
         free(array[i]);

     }
Manny_Mar
  • 398
  • 2
  • 13
  • Consider showing the code needed to free the allocated memory. – Jonathan Leffler Jan 21 '19 at 03:00
  • The thing is that I got an array initially (not an array of pointer), that is nothing I would like to mess with. – Yi Lin Liu Jan 21 '19 at 03:00
  • OK I freed the memory. – Manny_Mar Jan 21 '19 at 03:10
  • 1
    You can just use a 2d array and and the elements then, just `array[m][n]` and use a for loop to put the elements into the array. No `malloc` needed – Manny_Mar Jan 21 '19 at 03:12
  • While a good generic approach, usually, in my limited experience, two layers of indirection tends to be a tad slower than computing the linear index. And you shouldn't have any problems with syntax in light of this comment: https://stackoverflow.com/questions/54282732/c-convert-array-of-elements-into-2-d-matrix#comment95387369_54282732 – Mad Physicist Jan 21 '19 at 03:15