3

I have a 2D jagged array declared in my main() block. This is to be passed to a function to have memory allocated to it. The following is the most reduced case which compiles but crashes when it runs. Where am I going wrong?

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

void alloc2d(double ***p);


int main () {

    double **data;

    alloc2d(&data);
    printf("Before assign to data\n");
    data[0][0] = 0.1;
    printf("After assign to data\n");
    free(data);
}


void alloc2d(double ***p) {

    int i, n, m;

    // Get some dynamically assigned sizes
    printf("Enter size: ");
    scanf("%d %d", &n, &m);    
    // Now allocate
    *p = malloc(n * sizeof(double*));
    for (i = 0; i < n; i++) {
        *p[i] = malloc(m * sizeof(double));
    }
    printf("End of alloc2d\n");
}

This reads the values but crashes when I enter low numbers (i.e. '1 1') but crashes when I enter high numbers (i.e. '10 10').

Brendan
  • 18,771
  • 17
  • 83
  • 114

3 Answers3

7

You made a very simple syntax error

*p[i] = (double*)malloc(m * sizeof(double));

should really be

(*p)[i] = (double*)malloc(m * sizeof(double));

This is because in C, [] operator has higher precedence than *. So when you type *p[i], it is translated into **(p + i).

This means: you are asking the compiler to calculate the address by offsetting the address of p by i * sizeof(double**), which is clearly not what you actually want.

So, in order to force the compiler to dereference p first, simply surroud *p with brackets.

Jimmy Lu
  • 4,810
  • 7
  • 25
  • 30
  • Great thanks! Is that something to do with operator precedence? i.e. pointers are dereferenced after indexes are resolved? – Brendan Oct 18 '12 at 15:54
  • 1
    @Bredan, yes. :) [Here](http://www.swansontec.com/sopc.html) is a very handy operator precedence chart for your convenience. – Jimmy Lu Oct 18 '12 at 15:57
  • You're missing a `*` in your translation. `p[i]` already translates to `*(p+i)`, so `*p[i]` becomes `**(p+i)`. On the other hand, the offset would be `i*sizeof(double**)` bytes, so one of your stars fell down a few lines. – Daniel Fischer Oct 18 '12 at 19:31
  • Hmm, I'm not sure if I get what you mean. From my understanding, `p` is of type `double***`, so `p[i]` should be of type `double**`. Then, `(*p)[i]`, which is `**(p + i)`, would refer to `duoble*`. This should be right as OP is trying to assign an array of double's to it... Am I missing something here? – Jimmy Lu Oct 18 '12 at 19:39
  • And yeah, I agree with your second point, it should be offset'ed by `i * sizeof(double**)` :-) – Jimmy Lu Oct 18 '12 at 19:41
  • 1
    @BeyondSora No, `(*p)[i]` would be `*(*p + i)`. For any two expressions of admissible types (and values, so that the behaviour is defined), `e1[e2]` is equivalent to `*(e1 + e2)`. So `*p[i]` becomes `*(p[i]) = *(*(p+i))` - outer parentheses can be omitted -, you wrote "So when you type `*p[i]`, it is translated into `*(p + i)`", one asterisk is missing there. – Daniel Fischer Oct 18 '12 at 20:31
  • I see what you mean now. Thanks! – Jimmy Lu Oct 18 '12 at 21:17
3

Operator precedence is the answer. *p[i] is equivalent to *(p[i]). This makes you access memory that lies right after the data pointer, which will either corrupt some other variables on the stack, or crash completely.

You were looking for (*p)[i], which will be the i-th entry in the newly allocated array.

Jakub Wasilewski
  • 2,916
  • 22
  • 27
0

What your alloc2d() allocates is not really a 2D array, but:

  • 1 1D n-long array of pointers to double
  • n 1D m-long arrays of doubles

Multi-dimensional arrays in C are only possible, if all but the last of the dimensions are known at compile-time:

double a[5][11];

Maybe, this program can help you understand... Note, how COLUMNS is a compile-time constant, even if rows is a run-time variable:

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

typedef double  myrow_t[11]; /* 11 columns */
#define COLUMNS (sizeof(myrow_t)/sizeof(double))

static unsigned
alloc2d(myrow_t **pd)
{
unsigned int rows;

printf("Enter the number of rows: ");
while (scanf("%u", &rows) != 1)
    printf("\ninvalid input, please, try again: ");

*pd = malloc(rows * sizeof(**pd));
if (*pd == NULL)
    err(EX_TEMPFAIL, "Out of memory");

return rows;
}

int
main()
{
myrow_t     *d;
unsigned int     row, column, rows;

rows = alloc2d(&d);

for (row = 0; row < rows; row++)
    for (column = 0; column < COLUMNS; column++)
        d[row][column] = row * column;

for (row = 0; row < rows; row++) {
    printf("Row %3d:\t", row);
    for (column = 0; column < COLUMNS; column++)
        printf("%.0f\t", d[row][column]);
    puts("");
}

free(d);
return 0;
}
Mikhail T.
  • 3,043
  • 3
  • 29
  • 46