-3

This is part of an assignment. I currently at the part where I have to read in a .csv file into following struct:

typedef struct {
    int n;
    double **data;
} Matrix;

The .csv file has for example 3 rows and 3 columns:

1 2 3
4 5 6
7 8 9

and would look like this as a 2d array:

double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

and now I would want to assign my array to the Matrix like this:

function someFunction(Matrix *m) {
    //...
    double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

    A->data = array;
    A->n = 3;
    //...
}

But assigning array to data is not working as I get following error using gcc -Wall -pedantic-errors:

error: assignment to ‘double **’ from incompatible pointer type ‘double *’ [-Wincompatible-pointer-types] 79 | A->data = array;

How can I resolve this problem? Part of the assignment is that gcc is not allowed to show any warnings when using -Wall and -pedantic-erros, so hacks aren't allowed.

Angry Red Panda
  • 419
  • 1
  • 5
  • 28
  • 1
    Why not just use a double*, allocate the memory based on (rows * cols * sizeof(double)), then manage the index yourself with ((row * 3) + col) ? double array[] = (double*){{1,2,3},{4,5,6},{7,8,9}}; – SPlatten Jan 14 '20 at 09:37
  • @SPlatten the assignment requires me to use the above mentioned struct. so I have to use a double pointer. – Angry Red Panda Jan 14 '20 at 09:39
  • 1
    https://stackoverflow.com/questions/8203700/conversion-of-2d-array-to-pointer-to-pointer – sailfish009 Jan 14 '20 at 09:39
  • Keep it as it is but change your data declaration to be double* data, then assign with : A->data = (double*)array; Modify your structure to include a bit more detail, instead of n, add rows and cols both of which in this case will be 3. – SPlatten Jan 14 '20 at 09:41
  • @sailfish009 thanks that link solved my problem. – Angry Red Panda Jan 14 '20 at 09:43
  • 1
    Does this answer your question? [conversion of 2D array to pointer-to-pointer](https://stackoverflow.com/questions/8203700/conversion-of-2d-array-to-pointer-to-pointer) – Adrian Mole Jan 14 '20 at 09:44
  • 1
    @AngryRedPanda You need to be very careful if you are trying to follow that link. You can't define a local variable and assign it to your `data` pointer. The local variable is only valid inside the function. So you need to use dynamic memory allocation. – kaylum Jan 14 '20 at 09:47
  • `data` is not an array, nor is it a pointer to an array, nor can it be used as an array. See [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Jan 14 '20 at 11:30

1 Answers1

4

double** is a pointer to pointer. Of course the (outer) pointer could point to some array, too:

double* array[7];
double** pointer = array;

Now each of the pointers in array could point to other arrays as well:

double valueArray[12]
double* pointerArray[10];

double** pointer = array;
pointer[0] = valueArray; // equivalent to pointerArray[0] = ...

Two-dimenstional arrays are not arrays of pointers, though, they are arrays of arrays, and pointer to first element is of different type:

double array[10][12];
double(*pointer)[12] = array;

Trying to cover arbitrary size matrices produces some trouble, though:

struct Matrix
{
    size_t n;
    double(*data)[n]; // you cannot exchange arbitrary pointers,
                      // you need a compile time constant!
};

Be aware that this is different from VLA in function parameters, where you could have

void f(size_t n, int(*data)[n]);

Then it looks as if you get pretty much into trouble already with the bit of code you presented:

void someFunction(Matrix* m)
{
    double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

    m->data = array; // assuming you meant m instead of A
                     // and you did adjust the pointer type already appropriately
}

array has local storage duration and will be destroyed as soon as you return from the function, resulting in a dangling pointer in m and in undefined behaviour, if you dereference that pointer after returning from function.

So you would need to malloc an array of sufficient size instead. But don't forget to free it again then, too!

So if you don't want to go the pointer to pointer way:

double** array = malloc(sizeof(*array) * n);
array[0] = malloc(sizeof(**array) * n);
// ...

which would allow for the m->data[x][y] syntax, then you should stick to the one-dimensional array approach:

struct Matrix
{
    size_t n;
    double* data;
};

and maybe some accessor function:

double get(struct Matrix* m, size_t row, size_t column)
{
    return m->data[row * m->n + column];
}

which would get around the double pointer indirection and thus be faster (actually, that's exactly the same calculation that occurs under the hoods with a true two-dimensional array, i. e. not the pointer to pointer variant).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • For the last "mangled 2D array" version, you should use flexible array members, so that the data is allocated together with the struct. – Lundin Jan 14 '20 at 11:31
  • @Lundin Nice idea – suppose we should make `n` const then as well... But not suitable for *every* use case (e. g. arrays of matrices; enlarging matrix size would require creating new objects, ...). – Aconcagua Jan 14 '20 at 12:42