How to make a matrix squared by filling it with zeroes in C

Viewed 112

I am currently trying to complete a m by n matrix (where m is greater than or equal to n) by filling it with columns of zeroes. The main problem is that my matrix is stored inside a double* and not a double**. So instead of my matrix looking like a 2d matrix it actually is a vector that sticks the rows of the matrix together in a single line.

Here is my poor attempt (I am quite new to the C programming language...)

void square_my_matrix(double* A, int n){//m is a global variable
    for(int i = 0; i < m; i++){
        for(int j = 0; j < m; j++){
            if(i*m+j > j*m+n) continue;
            else A[i*m+j] = 0.0;
}

int main(){
    double* A;
    A = malloc(m*n*sizeof(*A));
    buildMatrix(); //this fills my matrix with some data
    printMatrix(m,n,A); //this prints my matrix in a 2d fashion to make it clearer

    if(m>n){
        A = realloc(A, m*m*sizeof(*A));
        square_my_matrix(A, n);
    }
    printMatrix(m,m,A);
}

My first printMatrix gives

1.00 1.00
1.00 3.00
1.00 5.00
1.00 7.00

And after calling square_my_matrix I expect (when calling printMatrix again)

1.00 1.00 0.00 0.00
1.00 3.00 0.00 0.00
1.00 5.00 0.00 0.00
1.00 7.00 0.00 0.00

but I get this instead

1.00 1.00 1.00 3.00
0.00 5.00 1.00 7.00
0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00

I probably messed up something obvious but I can't figure out what.

Thank you for your help

1 Answers

Zeroing Added Memory (New Rows)

If I understand what you are doing, you can originally initialize all values in A to 0 by using calloc instead of malloc (which will set all bytes zero). Then following your realloc you can use memset() to zero the new memory you have allocated. (if you need to zero the added columns, then see the 2nd part of this answer)

You can zero the new memory allocated with realloc() with, e.g.

double*A;
A = calloc (m * n, sizeof *A);
if (A == NULL) {          /* validate EVERY allocation */
    perror ("calloc-A");
    exit (EXIT_FAILURE);
}

buildMatrix(); //this fills my matrix with some data
printMatrix(m,n,A); //this prints my matrix in a 2d fashion to make it clearer

if (m > n) {
    void *tmp = realloc (A, m * m * sizeof *A);  /* ALWAYS realloc with a temporary pointer */
    if (!tmp) {           /* validate EVERY allocation */
        perror ("realloc-A");
        exit (EXIT_FAILURE);
    }
    A = tmp;              /* assign reallocated block */
    /* set new memory zero */
    memset (A + m * n, 0, m * (m - n) * sizeof *A);
    
    // square_my_matrix(A, n);
}
printMatrix(m,m,A);

If you did want to use square_my_matrix(), then you would remove memset() above and use:

void square_my_matrix (double* A, int n)
{
    for (int i = n; i < m; i++)
        for (int j = 0; j < m; j++)
                A[i * m + j] = 0.0;
}

A short example putting it altogether and allowing you to choose between memset() by passing -DWMEMSET as a define, or by default using square_my_matrix(), you could do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define m      10
#define nbegin  8

void square_my_matrix (double* A, int n)
{
    for (int i = n; i < m; i++)
        for (int j = 0; j < m; j++)
                A[i * m + j] = 0.0;
}

void printMatrix (int rows, int cols, double *A)
{
    /* check overflow in multiplication of rows * cols here */
    
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++)
            printf (" %3g", A[i * cols + j]);
                putchar ('\n');
    }
}

int main (void) {
    
    int n = nbegin,
        v = 1;
    double *A;
    
    A = calloc (m * n, sizeof *A);
    if (A == NULL) {          /* validate EVERY allocation */
        perror ("calloc-A");
        exit (EXIT_FAILURE);
    }
    
    // buildMatrix(); //this fills my matrix with some data
    for (int i = 0; i < m; i++)
        for (int j = 0; j < n; j++)
            A[i * n + j] = v++;
    
    
    printMatrix (m,n,A); //this prints my matrix in a 2d fashion to make it clearer
    
    if (m > n) {
        /* ALWAYS realloc with a temporary pointer */
        void *tmp = realloc (A, m * m * sizeof *A);
        if (!tmp) {           /* validate EVERY allocation */
            perror ("realloc-A");
            exit (EXIT_FAILURE);
        }
        A = tmp;              /* assign reallocated block */
#ifdef WMEMSET
        /* set new memory zero */
        memset (A + m * n, 0, m * (m - n) * sizeof *A);
#else
        square_my_matrix(A, n);
#endif
    }
    printMatrix (m,m,A);
    
}

Example Use/Output

The output is the same regarless whether WMEMSET is defined:

$ ./bin/mxn2mm
   1   2   3   4   5   6   7   8
   9  10  11  12  13  14  15  16
  17  18  19  20  21  22  23  24
  25  26  27  28  29  30  31  32
  33  34  35  36  37  38  39  40
  41  42  43  44  45  46  47  48
  49  50  51  52  53  54  55  56
  57  58  59  60  61  62  63  64
  65  66  67  68  69  70  71  72
  73  74  75  76  77  78  79  80
   1   2   3   4   5   6   7   8   9  10
  11  12  13  14  15  16  17  18  19  20
  21  22  23  24  25  26  27  28  29  30
  31  32  33  34  35  36  37  38  39  40
  41  42  43  44  45  46  47  48  49  50
  51  52  53  54  55  56  57  58  59  60
  61  62  63  64  65  66  67  68  69  70
  71  72  73  74  75  76  77  78  79  80
   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0

Zeroing Only Last Columns

If you need to zero the added columns instead of the rows, then you will need to manually copy the original rows to the rows of a newly allocated block and then free the original. To do so, you will have to change square_my_matrix() to take the address of the pointer (a pointer-to-pointer) rather than a simple pointer which will allow you to update where A points within your function. For example:

void square_my_matrix (double **A, int n)
{
    double *b = calloc (m * m, sizeof *b);
    if (!b) {
        perror ("calloc-b");
        exit (EXIT_FAILURE);
    }
    
    for (int i = 0; i < m; i++)
        memcpy (b + i * m, *A + i * n, n * sizeof **A);
    
    free (*A);
    *A = b;
}

In essence, you are writing the custom realloc().

and then you would call the function as:

    square_my_matrix (&A, n);

Example Output

Then your output would be:

$ ./bin/mxn2mm
   1   2   3   4   5   6   7   8
   9  10  11  12  13  14  15  16
  17  18  19  20  21  22  23  24
  25  26  27  28  29  30  31  32
  33  34  35  36  37  38  39  40
  41  42  43  44  45  46  47  48
  49  50  51  52  53  54  55  56
  57  58  59  60  61  62  63  64
  65  66  67  68  69  70  71  72
  73  74  75  76  77  78  79  80
   1   2   3   4   5   6   7   8   0   0
   9  10  11  12  13  14  15  16   0   0
  17  18  19  20  21  22  23  24   0   0
  25  26  27  28  29  30  31  32   0   0
  33  34  35  36  37  38  39  40   0   0
  41  42  43  44  45  46  47  48   0   0
  49  50  51  52  53  54  55  56   0   0
  57  58  59  60  61  62  63  64   0   0
  65  66  67  68  69  70  71  72   0   0
  73  74  75  76  77  78  79  80   0   0

For completion, the updated main() in the example above would reduce to:

int main (void) {
    
    int n = nbegin,
        v = 1;
    double *A;
    
    A = calloc (m * n, sizeof *A);
    if (A == NULL) {          /* validate EVERY allocation */
        perror ("calloc-A");
        exit (EXIT_FAILURE);
    }
    
    // buildMatrix(); //this fills my matrix with some data
    for (int i = 0; i < m; i++)
        for (int j = 0; j < n; j++)
            A[i * n + j] = v++;
    
    printMatrix (m,n,A);
    if (m > n)
        square_my_matrix (&A, n);
    printMatrix (m,m,A);
}

Look things over and let me know if you have further questions.

Related