简体   繁体   中英

C - Convert array of elements into 2-d matrix

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.)

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

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 (ie 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.

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.

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]);

     }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM