You're not using anything from either <math.h>
or <complex.h>
— you're using your own dcomplex
type. Those headers may as well not be present.
The nested function notation is an abomination — GCC allows it, but it shouldn't, and you should not use the notation.
When the functions are unnested (I decline to find out what happens when they are nested), the compiler complains bitterly and justly:
cx19.c: In function ‘main’:
cx19.c:76:42: error: passing argument 4 of ‘print_matrix’ from incompatible pointer type [-Werror=incompatible-pointer-types]
print_matrix("original H is: \n",8,4,H);
^
cx19.c:15:6: note: expected ‘dcomplex (*)[(sizetype)(n)]’ but argument is of type ‘dcomplex ** {aka struct str_dcomplex **}’
void print_matrix(char* desc, int m, int n, dcomplex a[m][n]) {
^~~~~~~~~~~~
cx19.c:77:47: error: passing argument 4 of ‘print_matrix’ from incompatible pointer type [-Werror=incompatible-pointer-types]
print_matrix("original result is: \n",8,4,result);
^~~~~~
You're passing a dcomplex **
and pretending it is a dcomplex[m][n]
type. I'm sorry to inform you that the compiler is correct and your code is wrong.
If you want to pass variable-length 2D arrays, you'll have to allocate variable-length 2D arrays. If you want to pass pointers to pointers, you'll have to revise the functions.
Using a dynamically allocated array of pointers
Changing the functions is easy; simply change the type of the relevant arguments. Note that there are no other necessary changes to the functions than the change in the function signature.
#include <stdio.h>
#include <stdlib.h>
struct str_dcomplex
{
double re;
double im;
};
typedef struct str_dcomplex dcomplex;
static
void print_matrix(char* desc, int m, int n, dcomplex **a) {
int i, j;
printf("\n%s\n",desc);
for(i = 0; i < m; i++) {
for(j = 0; j < n; j++) {
printf("(%5.1f,%5.1f)",a[i][j].re, a[i][j].im);
}
putchar('\n');
}
}
static
void matrix_copy(int r, int c, dcomplex **source, dcomplex **dest) {
int i, j;
for(i = 0; i < r; i++) {
for(j = 0; j < c; j++) {
dest[i][j].re = source[i][j].re;
dest[i][j].im = source[i][j].im;
}
}
}
int main(void) {
int p,q;
int i,j;
// memory allocation for H
dcomplex **H;
H = (dcomplex**)malloc(sizeof(dcomplex*)*8);
for(p = 0; p < 8; p++) {
H[p] = (dcomplex*)malloc(sizeof(dcomplex)*4);
}
for(p = 0; p < 8; p++) {
for(q = 0; q < 4; q++) {
H[p][q].re = 1.1;
H[p][q].im = 0.0;
}
}
dcomplex** result;
result = (dcomplex**)malloc(sizeof(dcomplex*)*8);
for(p = 0; p < 8; p++) {
result[p] = (dcomplex*)malloc(sizeof(dcomplex)*4);
}
for(p = 0; p < 8; p++) {
for(q = 0; q < 4; q++) {
result[p][q].re = 0.0;
result[p][q].im = 2.2;
}
}
// print H and result
print_matrix("original H is: \n",8,4,H);
print_matrix("original result is: \n",8,4,result);
// copy H to result
matrix_copy(8,4,H,result);
// print H and result
print_matrix("new H is: \n",8,4,H);
print_matrix("new result is: \n",8,4,result);
int count = 0;
for(i = 0; i < 8; i++) {
for(j = 0; j < 4; j++){
if((H[i][j].re == result[i][j].re) && (H[i][j].im == result[i][j].im))
count++;
}
}
printf("\n%d\n\n",count);
// free the memory
for(i = 0; i < 8; i++) {
free(H[i]);
}
free(H);
for(i = 0; i < 8; i++) {
free(result[i]);
}
free(result);
return 0;
}
The output from that is:
original H is:
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
original result is:
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)( 0.0, 2.2)
new H is:
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
new result is:
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)( 1.1, 0.0)
32
Using dynamically allocated variable-length arrays
It's also perfectly feasible to dynamically allocate the variable-length arrays, and pass those to the functions. This leads to code like this:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
struct str_dcomplex
{
double re;
double im;
};
typedef struct str_dcomplex dcomplex;
static void print_matrix(char *desc, int m, int n, dcomplex a[m][n])
{
int i, j;
printf("\n%s\n", desc);
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
printf("(%5.1f,%5.1f)", a[i][j].re, a[i][j].im);
putchar('\n');
}
}
static void matrix_copy(int r, int c, dcomplex source[r][c], dcomplex dest[r][c])
{
int i, j;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
dest[i][j] = source[i][j];
}
}
int main(void)
{
int p, q;
int i, j;
// memory allocation for H
dcomplex(*H)[4] = malloc(sizeof(H[0]) * 8);
assert(H != 0); // Sloppy!
for (p = 0; p < 8; p++)
{
for (q = 0; q < 4; q++)
H[p][q] = (dcomplex){ .re = 1.1, .im = 0.0 };
}
dcomplex (*result)[4] = malloc(sizeof(result[0]) * 8);
assert(result != 0); // Sloppy!
for (p = 0; p < 8; p++)
{
for (q = 0; q < 4; q++)
result[p][q] = (dcomplex){ .re = 0.0, .im = 2.2 };
}
// print H and result
print_matrix("original H is:", 8, 4, H);
print_matrix("original result is:", 8, 4, result);
// copy H to result
matrix_copy(8, 4, H, result);
// print H and result
print_matrix("new H is:", 8, 4, H);
print_matrix("new result is:", 8, 4, result);
int count = 0;
for (i = 0; i < 8; i++)
{
for (j = 0; j < 4; j++)
{
if ((H[i][j].re == result[i][j].re) && (H[i][j].im == result[i][j].im))
count++;
}
}
printf("\n%d\n\n", count);
// free the memory
free(H);
free(result);
return 0;
}
The code also uses some compound literals with designated initializers, and loosely error checks the memory allocation. Using assert()
for error checking isn't sensible in production code; this isn't production code and it suffices.
The output is very similar — there isn't a blank line between the matrix description and the matrix data.
Using regular variable-length arrays
#include <stdio.h>
struct str_dcomplex
{
double re;
double im;
};
typedef struct str_dcomplex dcomplex;
static void print_matrix(char *desc, int m, int n, dcomplex a[m][n])
{
printf("\n%s\n", desc);
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("(%5.1f,%5.1f)", a[i][j].re, a[i][j].im);
putchar('\n');
}
}
static void matrix_copy(int r, int c, dcomplex source[r][c], dcomplex dest[r][c])
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
dest[i][j] = source[i][j];
}
}
static void matrix_init(int r, int c, dcomplex target[r][c], dcomplex value)
{
for (int p = 0; p < r; p++)
{
for (int q = 0; q < c; q++)
target[p][q] = value;
}
}
int main(void)
{
int r = 8;
int c = 4;
dcomplex H[r][c];
matrix_init(r, c, H, (dcomplex){ .re = 1.1, .im = 0.0 });
dcomplex result[r][c];
matrix_init(r, c, result, (dcomplex){ .re = 0.0, .im = 2.2 });
// print H and result
print_matrix("original H is:", r, c, H);
print_matrix("original result is:", r, c, result);
// copy H to result
matrix_copy(r, c, H, result);
// print H and result
print_matrix("new H is:", r, c, H);
print_matrix("new result is:", r, c, result);
int count = 0;
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
if ((H[i][j].re == result[i][j].re) && (H[i][j].im == result[i][j].im))
count++;
}
}
printf("\n%d\n\n", count);
return 0;
}
The change to using variable length arrays is simple, and means we no longer need to use <stdlib.h>
. I created the matrix_init()
function to initialize the arrays — it cuts down on repetition in the main()
function. If you use array sizes determined at run-time, it is important to do sanity checks on the array size; there is a limit on how much space is available on the stack. However, the size of these arrays (8 * 4 * 2 * sizeof(double)
, which is usually 512 bytes) isn't going to stress any modern desktop machine.
The change to using classic fixed size arrays is trivial. The changes to set the values of r
and c
is likewise basically trivial.