Home > OS >  How can I give a struct a pointer to the first element of a matrix in C, and then print different el
How can I give a struct a pointer to the first element of a matrix in C, and then print different el

Time:12-06

I wanted to have a struct in C with the dimensions and a pointer to the first element of a matrix, and then use that pointer to print out different elements of the same matrix. Instead it doesn't print out anything and gives a warning when I try to compile my code.

The code:

#include <stdio.h>

typedef struct Matrix
{
    int height; // matrix height, first dim
    int width; // matrix width, second dim
    int** firstE; // first matrix elem
} matrix;


int main()
{
    int m1[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; //example of two-dimensional array
    matrix matrix_1 = {3,3,m1};
    
    printf("%d\n",**(matrix_1.firste));     //should output 1
    printf("%d\n",**(matrix_1.firste   1)); //should output 4
    printf("%d\n",*(*(matrix_1.firste) 1)); //should output 2
    
    
    return 0;
}

The warning itself:

.\example.c:14:32: warning: initialization of 'int **' from incompatible pointer type 'int (*)[3]' [-Wincompatible-pointer-types]
   14 |         matrix matrix_1 = {3,3,m1};

I figured the code I made would work, because the code below does what I intend to do. What I expected was that I could give "m1" to the struct above as "int**".

#include <stdio.h>

int main()
{
    int m1[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; //example of two-dimensional array
    
    printf("%d\n",**(m1)); //should output 1
    printf("%d\n",**(m1   1)); //should output 4
    printf("%d\n",*(*(m1) 1)); //should output 2
    
    return 0;
}

CodePudding user response:

It is a very common misunderstanding in C. Array of pointers in not a 2D array.

In your case you need to assign pointers to int arrays to an array of pointers to int:

int main(void)
{
    int *m1[3] = {(int[]){1,2,3},(int[]){4,5,6},(int[]){7,8,9}}; //example of two-dimensional array
    matrix matrix_1 = {3,3,m1};
    
    printf("%d\n",**(matrix_1.firstE));     //should output 1
    printf("%d\n",**(matrix_1.firstE   1)); //should output 4
    printf("%d\n",*(*(matrix_1.firstE) 1)); //should output 2
    
    return 0;
}

https://godbolt.org/z/vMM5aaqc9

As a side note: use size_t not int for sizes

CodePudding user response:

Pointers and arrays have a close relationship, but they are completely different things. Therefore, although you can form an array of pointers or an array of arrays, these, too, are different things. C 2D arrays are of the latter variety -- arrays of arrays. The corresponding pointer type is a pointer to an array, not a pointer to a pointer, which is exactly what your error message is telling you.

It would be possible to write your struct Matrix so that the initialization you present would work, but if the accesses in the printf statements are also to work as expected then that would require a fixed width for the matrix to be defined in struct Matrix. Example:

typedef struct Matrix
{
    int height;       // matrix height, first dim
    int width;        // mostly irrelevant
    int (*firstE)[3]; // first matrix row
} matrix;

// ...

    int m1[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; //example of two-dimensional array
    matrix matrix_1 = {3,3,m1};

However, for what you seem to have in mind, you probably want a single pointer to the first element of the first row. This will require you to perform indexing calculations manually:

typedef struct Matrix
{
    int height; // matrix height, first dim
    int width; // matrix width, second dim
    int *firstE; // first element
} matrix;


int main()
{
    int m1[3][3] = { { 1, 2, 3 },
                     { 4, 5, 6 },
                     { 7, 8, 9 } }; //example of two-dimensional array
    matrix matrix_1 = { 3, 3, m1[0] };
    
    printf("%d\n",*(matrix_1.firstE   matrix_1.width * 0   0)); //should output 1
    printf("%d\n",*(matrix_1.firstE   matrix_1.width * 1   0)); //should output 4
    printf("%d\n",*(matrix_1.firstE   matrix_1.width * 0   1)); //should output 2
    
    return 0;
}

That shows the full index calculation in each case, but of course you could simplify each one. In practice, however, I expect that you would write a macro or maybe a function to obtain elements, for clarity and to reduce the risk of mistakes:

#define MATRIX_ELEMENT(m, r, c) ((m).firstE[(m).width * (r)   (c)])

// ...

    printf("%d\n", MATRIX_ELEMENT(matrix_1, 0, 0)); //should output 1
    printf("%d\n", MATRIX_ELEMENT(matrix_1, 1, 0)); //should output 4
    printf("%d\n", MATRIX_ELEMENT(matrix_1, 0, 1)); //should output 2

You can assign to that, too:

    MATRIX_ELEMENT(matrix_1, 1, 1) = 42;

Do note, however, that that macro evaluates the expression designating the matrix twice, which is an issue if that expression has any side effects. I don't see a way to avoid that in a function-like macro -- you would need to go with an actual function, which would inherently resolve that issue:

int matrix_element(matrix *m, size_t r, size_t c) {
    return m->firstE[m->width * r   c];
}

// ...

    printf("%d\n", matrix_element(matrix_1, 0, 0)); //should output 1
    printf("%d\n", matrix_element(matrix_1, 1, 0)); //should output 4
    printf("%d\n", matrix_element(matrix_1, 0, 1)); //should output 2

In that case, you would need a separate function for storing elements.

On the third hand, you could conceivably combine a (single) function and a macro to avoid the problem with side effects and also avoid the need for multiple functions:

int *matrix_element_p(matrix *m, size_t r, size_t c) {
    return m->firstE   m->width * r   c;
}

#define MATRIX_ELEMENT(m, r, c) (*matrix_element_p(m, r, c))

// ...
    MATRIX_ELEMENT(matrix_1, 1, 1) = 42;

    printf("%d\n", MATRIX_ELEMENT(matrix_1, 0, 0)); //should output 1
    printf("%d\n", MATRIX_ELEMENT(matrix_1, 1, 0)); //should output 4
    printf("%d\n", MATRIX_ELEMENT(matrix_1, 0, 1)); //should output 2
    printf("%d\n", MATRIX_ELEMENT(matrix_1, 1, 1)); //should output 42

CodePudding user response:

The problem is that the element of int[3][3] matrix is an int[3] array. So the type of the member must be a pointer to this array. The meber type must be int(*)[3]. Unfortunately, the size of inner array is going fixed. A workaround is using a pointer to incomplete array type int(*)[]. The disadvantage of incomplete types is that they cannot be dereferenced. Therefore, the original type has to be reconstructed, preferably by using a pointer to VLA.

#include <stdio.h>

typedef struct Matrix
{
    int height; // matrix height, first dim
    int width; // matrix width, second dim
    int (*arr)[]; // first matrix row
} matrix;

void print_matrix(matrix m) {
  int (*mat)[m.width] = m.arr;
  for (int y = 0; y < m.height;   y) {
   for (int x = 0; x < m.width;   x)
    printf("%d ", mat[y][x]);
   puts("");
  }
}

int main() {
  int m1[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; //example of two-dimensional 
  matrix m = { 3, 3, m1 };
  print_matrix(m);
}

It compile without a warning and works perfectly. See godbolt.

  • Related