Home > other >  Matrix creation / value setting / value copying in C using functions and return values
Matrix creation / value setting / value copying in C using functions and return values

Time:10-27

I have a task of creating a matrix, that is the size of NxN, where N is a given parameter. The matrix should be filled with random 0s and 1s.

I have tried the following code:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
#include <pthread.h>

int** createMatr(int N){
    int** matr[10][10];
    for(int i = 0; i < N; i  ){
        for(int j = 0; j < N; j  ){
            int rnd = rand() % 2;
            matr[i][j] = rnd;
        }
    }
    return matr;
}

void setValMatr(int N, int** matr[][10], int** newMatr[][10]){
    for(int i = 0; i < N; i  ){
        for(int j = 0; j < N; j  ){
            newMatr[i][j] = matr[i][j];
        }
    }
}

void printMatr(int N, int** matr[][10]){
    for(int i = 0; i < N; i  ){
        for(int j = 0; j < N; j  ){
            printf("%d ",matr[i][j]);
        }
        printf("\n");
    }
}

int main(int argc, char* argv[]){
    if(argc!=2){
        perror("parameter error\n");
    }
    int N = atoi(argv[1]);

    int** matrix[10][10];
    int** helper[10][10];
    
    setValMatr(N,createMatr(N), matrix);
    setValMatr(N,matrix,helper);

    printMatr(N, matrix);

    return 0;
}

The compilation warnings this gives me are:

┌──(kali㉿kali)-[~/Desktop/Cprog/Linux2_Lab2]
└─$ gcc gvim2135_L2_1.c -o p
gvim2135_L2_1.c: In function ‘createMatr’:
gvim2135_L2_1.c:15:24: warning: assignment to ‘int **’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
   15 |             matr[i][j] = rnd;
      |                        ^
gvim2135_L2_1.c:18:12: warning: returning ‘int ** (*)[10]’ from a function with incompatible return type ‘int **’ [-Wincompatible-pointer-types]
   18 |     return matr;
      |            ^~~~
gvim2135_L2_1.c:18:12: warning: function returns address of local variable [-Wreturn-local-addr]
gvim2135_L2_1.c: In function ‘main’:
gvim2135_L2_1.c:47:18: warning: passing argument 2 of ‘setValMatr’ from incompatible pointer type [-Wincompatible-pointer-types]
   47 |     setValMatr(N,createMatr(N), matrix);
      |                  ^~~~~~~~~~~~~
      |                  |
      |                  int **
gvim2135_L2_1.c:21:30: note: expected ‘int ** (*)[10]’ but argument is of type ‘int **’
   21 | void setValMatr(int N, int** matr[][10], int** newMatr[][10]){
      |                        ~~~~~~^~~~~~~~~~

After running I get the error:

Segmentation fault

CodePudding user response:

With the int** matrix[][] notation you are not create a matrix but a matrix of matrix pointer. You can see a matrix as a pointer to an array of array: int** matrix.

So, your code become:

int** create_matrix(int size) {
  int i, j, **matrix = (int **)malloc(size * sizeof(int*));
  for (i = 0; i < size;   i) {
    matrix[i] = (int *)malloc(size * sizeof(int));
    for (j = 0; j < size;   j) {
      matrix[i][j] = rand() % 2;
    }
  }

  return matrix;
}

void print_matrix(int** matrix, int size) {
  int i, j;
  for (i = 0; i < size;   i) {
    for (j = 0; j < size;   j) {
      printf("%d | ", matrix[i][j]);
    }
    printf("\n");
  }
}

CodePudding user response:

Use a structure to describe matrices in general, for example

typedef  struct imatrix  imatrix;
struct imatrix {
    int   rows;
    int   cols;
    int  *data;
};

static inline int  imatrix_get(const imatrix *im, int row, int col, int outside)
{
    if (!im || row < 0 || col < 0 || row >= im->rows || col >= im->cols)
        return outside;
    else
        return im->data[row * (size_t)(im->cols)   col];
}

static inline void  imatrix_set(imatrix *im, int row, int col, int value)
{
    if (im && row >= 0 && col >= 0 && row < im->rows && col < im->cols)
        im->data[row * (size_t)(im->cols)   col] = value;
}

void imatrix_free(imatrix *im)
{
    if (im) {
        free(im->data);
        im->rows = 0;
        im->cols = 0;
        im->data = NULL;
    }
}

int imatrix_new(imatrix *im, int rows, int cols)
{
    if (!im)
        return -1;  /* No matrix specified */

    im->rows = 0;
    im->cols = 0;
    im->data = NULL;

    if (rows < 1 || cols < 1)
        return -2;  /* Invalid size */

    const size_t  rowsize = (size_t)cols * sizeof im->data[0];
    const size_t  datasize = (size_t)rows * rowsize;
    if ((size_t)(datasize / rows) != rowsize ||
        (size_t)(rowsize / cols) != sizeof im->data[0])
        return -3;  /* Matrix is too large */

    im->data = malloc(datasize);
    if (!im->data)
        return -4;  /* Not enough memory available */

    im->rows = rows;
    im->cols = cols;
    return 0;
}

The idea is that imatrix is a structure that contains the number of rows and columns in the matrix, and a pointer to the (array of) data elements. If m is a properly initialized imatrix, element at row r, column c is m->data[r*(size_t)(m->cols) c].

Note that the int type is not large enough on many architectures to for the index, so we need to cast the number of columns to the proper type, size_t (which is the proper type for all in-memory sizes and offsets).

If you have e.g. imatrix m;, you need to initialize it (and dynamically allocate memory for it, say 30 rows and 20 columns) by calling imatrix_new(&m, 30, 20);. It will return 0 if successful, and negative error code if it fails.

The imatrix_get() and imatrix_set() functions are convenient accessor functions, that will not try to access the matrix contents outside of bounds. You do not need to use them in your own functions, if you make sure your loops etc. are always within range; you can then safely use the im->data[r * (size_t)(im->cols) c] idiom.

When you no longer need the matrix, you discard it with a simple imatrix_free(&m) call.

  • Related