1

I have problem to correctly interpret two different ways of dynamically allocation 2D arrays in C.

This first method reads (for readability, I left out the if(arr==NULL) checks):

double** matrix_d( long int Nrows, long int Ncols ) {
    long int  ii;
    double**  arr;
    // allocate pointer to rows 
    arr = calloc( Nrows , (sizeof *arr));        
    for( ii=0 ; ii < Nrows; ii++) {
        // allocate pointer to each column (?)
        arr[ii] = calloc( Ncols , (sizeof **arr) );
    }
    return arr;
}

The second method reads (again, omitting the check of the return-value of calloc):

double** matrix_d2( long int Nrows, long int Ncols ) {
    long int  ii;
    double**  arr;
    // allocate pointers to rows
    arr = calloc( Nrows , (sizeof *arr) );
    // allocate rows (?)
    arr[0] = calloc( Nrows*Ncols, (sizeof arr) );
    // set pointers to rows (?)
    for( ii=0 ; ii < Nrows; ii++)
        arr[ii] = (*arr + Ncols*ii);
    return arr;

The comment-lines reveal probably my lack of understanding the memory allocation properly... especially the second method is somehow confusing to me (but seems to be "better" in that sense that it requires only 2 calloc/malloc calls).

Could one of you probably point me to the correct interpretation? That would be greatly appreciated! }

EDIT: there was a typo in both methods' first calloc call

Alf
  • 1,821
  • 3
  • 30
  • 48
  • `arr = calloc( Nrows , (sizeof arr));` should be `arr = calloc(Nrows, sizeof *arr);` in both examples. – alk Nov 15 '15 at 18:11
  • The first example allocates `Nrow + 1` probably not continuous blocks of memory, whereas the the second creates only two and with this leads to less memory fragmentation. – alk Nov 15 '15 at 18:16
  • 2
    Bad star count. Here's a rule of thumb: `x = calloc(n, sizeof(*x))`. If your code doesn't match this pattern, use your thumb to poke the keyboard until it does. – n. m. could be an AI Nov 15 '15 at 18:16
  • "*`//set pointers to rows `*" is the correct interpretation. Whereas in the 2nd case this "*`// allocate rows`*" should read `"*// allocate rows and columns`*". – alk Nov 15 '15 at 18:18
  • 2
    There is no 2D array in your code. **A pointer is not an array.** – too honest for this site Nov 15 '15 at 18:20
  • 1
    @alk thanks, that was a typo – Alf Nov 15 '15 at 18:22
  • 2
    OT: All these `long int`s should better be `size_t`s or at least be unsigned. – alk Nov 15 '15 at 18:24
  • Learn how to use real 2D arrays and not pseudo replacements for them. Then you can do it with just one `malloc` or `calloc` call and don't even need a special function for that. – Jens Gustedt Nov 15 '15 at 21:02
  • @JensGustedt hmm, could you give me a small hint/link on what you mean by "real 2D arrays" ...? Thanks. – Alf Nov 15 '15 at 21:20
  • Instead of `double **arr = matrix_d(x, y);` you can write `double (*arr)[y] = calloc(x * y, sizeof(double));` . There are pros and cons to each method, but this one is simpler if you don't have a firm reason for preferring the other. `matrix_d2` is not the best option however. – M.M Nov 16 '15 at 00:13
  • `arr[0] = calloc( Nrows*Ncols, (sizeof arr) )` should be `sizeof **arr` – M.M Feb 26 '17 at 08:58

1 Answers1

2

Assuming the matix_d is clear.

The second function creates two areas of memory. One to store pointers to the columns and one to hold the data itself.

Every "position" in the of the first level of the array a position in the second area of memory is stored.

The method of storing and collecting the second area of memory is a little confusing. First arr[0] is assigned and is retrieved with *arr. Which is the same.

Also, when adding 1 to a pointer will increase the pointer value with the size of the data the pointer is pointing to. So ((double*)0) + 1 is the same as ((double*)sizeof(double)).

To my opinion it is better to use a local pointer to the second area of memory and use that one in the for loop.

Secondly, the sizeof is wrong. You want to allocate (cols * rows) of doubles. Use sizeof(**arr) or just sizeof(double).

double** matrix_d2( long int Nrows, long int Ncols ) {
    long int  ii;
    double* data;
    double**  arr;
    // allocate pointers to rows
    arr = calloc( Nrows , (sizeof arr) );
    // allocate data rows * cols
    data = calloc( Nrows*Ncols, (sizeof **arr) );
    // set pointers to rows
    for( ii=0 ; ii < Nrows; ii++)
        arr[ii] = (data + (Ncols*ii));
    return arr;

Does this help you with the interpretation of the code?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pieter
  • 124
  • 4
  • Thanks for your answer. Firstly, you are of course right with the wrong `sizeof`. Secondly, sounds like you are recommending the first method (`matrix_d`) as it is a bit easier to understand. (Sometimes it is better to use what you understand than to use what you do not really understand but seems to be better in some performance stats). – Alf Nov 15 '15 at 19:24