Consider a data structure to describe blocks of a matrix in C:
// Matrix block
typedef struct {
union {
int i;
double d;
} *a; // pointer to first element in matrix block
int k; // block index
int p; // no. of block rows
int q; // no. of block columns
int m; // no. of matrix rows
int n; // no. of matrix columns
int r; // MPI rank of block owner
} mtrx_blk;
I would like to use a union for the pointer to the first element of a matrix block. I would like to use the same mtrx_blk
structure for different matrix types. In my case matrices can be either of type double
or int
. For example, if I create two matrix blocks with NBLK = 2;
and a matrix A
of type double
:
// for each block
for (int k = 0; k < NBLK; k++) {
blk[k] = (mtrx_blk) { .a = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
}
Matrix A is of size M x N elements and matrix blocks are of size P x Q elements.
The above way to assign to a pointer .a
results in an error:
gcc -std=c11 -pedantic-errors struct-union.c -o struct-union
struct-union.c: In function 'main':
struct-union.c:52:36: error: initialization of 'union <anonymous> *' from incompatible pointer type 'double *' [-Wincompatible-pointer-types]
52 | blk[k] = (mtrx_blk) { .a = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
| ^
struct-union.c:52:36: note: (near initialization for '(anonymous).a')
I tried to phrase it as:
// for each block
for (int k = 0; k < NBLK; k++) {
blk[k] = (mtrx_blk) { .a.d = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
}
But this also fails to compile with
gcc -std=c11 -pedantic-errors struct-union.c -o struct-union
struct-union.c: In function 'main':
struct-union.c:52:31: error: field name not in record or union initializer
52 | blk[k] = (mtrx_blk) { .a.d = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
| ^
struct-union.c:52:31: note: (near initialization for '(anonymous)')
struct-union.c:52:38: error: initialization of 'union <anonymous> *' from incompatible pointer type 'double *' [-Wincompatible-pointer-types]
52 | blk[k] = (mtrx_blk) { .a.d = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
| ^
struct-union.c:52:38: note: (near initialization for '(anonymous).a')
My questions are:
.a.i = &B[k*P*Q]
for a matrix B
of an integer data type?Please find the full source code below:
#include <stdlib.h>
// Matrix block
typedef struct {
union {
int i;
double d;
} *a; // pointer to first element in matrix block
int k; // block index
int p; // no. of block rows
int q; // no. of block columns
int m; // no. of matrix rows
int n; // no. of matrix columns
int r; // MPI rank of block owner
} mtrx_blk;
int main(int argc, char *argv[]) {
// no. of matrix rows
int const M = 10;
// no. of matrix columns
int const N = 10;
// no. of blocks
int const NBLK = 2;
// no. of block rows
int const P = M/NBLK;
// no. of block columns
int const Q = N;
// allocate memory for matrix
double *A = (double*) malloc(M*N*sizeof(double));
// allocate array of blocks
mtrx_blk *blk = (mtrx_blk*) malloc(NBLK*sizeof(mtrx_blk));
// for each block
for (int k = 0; k < NBLK; k++) {
blk[k] = (mtrx_blk) { .a.d = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
}
// free memory of array of blocks
free(blk);
// free memory of matrix
free(A);
return 0;
}
I would like to use a union for the pointer to the first element of a matrix block. I would like to use the same
mtrx_blk
structure for different matrix types. In my case matrices can be either of typedouble
orint
.
In that case, you have not expressed your intent correctly.
This ...
union { int i; double d; } *a
... declares a
as a pointer to a union. It would be matched to an array of such unions,* and probably (though not necessarily) usable with an array of double
. But it is unlikely to be appropriate for use with an array of int
, given the likelihood that the representation of int
on your system is smaller than the representation of double
.
- How do I do it correctly?
A fairly low-impact correction would be to replace your pointer to a union with a union of pointers. For example:
typedef struct {
union {
int *as_int;
double *as_double;
} a; // pointer to first element in matrix block
int k; // block index
int p; // no. of block rows
int q; // no. of block columns
int m; // no. of matrix rows
int n; // no. of matrix columns
int r; // MPI rank of block owner
} mtrx_blk;
You might then write something like:
blk[k] = (mtrx_blk) { .a = { .as_double = &A[k*P*Q] }, .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
Alternatively, yes, you can simplify that slightly to
blk[k] = (mtrx_blk) { .a.as_double = &A[k*P*Q], .k = k, .p = P, .q = Q, .m = M, .n = N, .r = k };
That's a valid form, at least. I can't evaluate whether the specifics are correct for what you are trying to achieve.
There are other possibilities, too, some of which might serve your purposes better. For instance, if you structure it as a union of mostly-congruent, tagged or typedefed structures, then you could have a distinct type for each element type, yet be able to handle them jointly via the union. That could unlock some convenience via type-_Generic
macros.
- Is my logic correct and can I use the analogous syntax
.a.i = &B[k*P*Q]
for a matrix B of an integer data type?
You can initialize the int *
member of the union instead of the double *
member, yes.
*subject to some provisos related to the scope of the union type