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.