-2

I am trying to simulate matrices using 2d arrays. But I am stuck at a run-time error. I am sure it occurs at printing function,but I couldn't find a solution at all.

obtn_matrix is a function that I use to create a matrix array. I didn't share the filler function which is used to get int values for the elements of the matrix array,so printed values will be whats in the memory. However, the problem is, I can't print the values at all. Program crashes after obtn_matrix.

int main()
{
    int**matrix,m,n;

    obtn_matrix(matrix,m,n);
    prnt_matrix(matrix,m,n);
    getch();
}
void prnt_matrix(int** matrix,int m,int n)
{

    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            printf("%d ",matrix[i][j]);
        }
        printf("\n");
    }
}
void obtn_matrix(int** matrix,int m,int n)
{
    printf("Please enter the column number: ");
    fflush(stdin);
    scanf("%d",&m);

    printf("Please enter the row number: ");
    fflush(stdin);
    scanf("%d",&n);
    matrix=create_matrix(m,n);
}

The expected result is something like below:

4542 64 274 4234
765  53 3523 5345
5145 154 545 545
5435 543 545 14

I will handle the formatting (%4d etc). Kindly thanks in advance.

Govind Parmar
  • 18,500
  • 6
  • 49
  • 78
Hakan Demir
  • 65
  • 1
  • 1
  • 9
  • 1
    There is no `"array"` to be found anywhere in your code. You are using a *pointer-to-pointer-to-*`int` to simulate a 2D array. You will also want to look at [Using fflush(stdin)](https://stackoverflow.com/questions/2979209/using-fflushstdin) though since you are using `getch()` windows is a non-standard exception to the general rule... – David C. Rankin Feb 15 '19 at 21:32
  • `obtn_matrix()` does not return the values for `matrix`, `m` & `n`. Therefore they are still undefined when you call `prnt_matrix()` from your `main()`. You should change to `void obtn_matrix(int*** matrix,int *m,int *n)` and `*matrix = create_matrix(*m, *n);`. And please do yourself a favor and enable the compiler warnings... (e.g `gcc -Wall`) – Stefan Becker Feb 15 '19 at 21:35

3 Answers3

1

in obtn_matrix the assignment of matrix, m and n are not visible from main , you need to use pointer to them.

For instance, using array of int * because the dimensions are unknown at compile time:

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

void prnt_matrix(int ** matrix,int m,int n)
{

    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            printf("%d ",matrix[i][j]);
        }
        printf("\n");
    }
}

int ** create_matrix(int m, int n)
{
  int ** ma = calloc(m, sizeof(int*));

  if (ma != NULL) {
    int i, j;

    for (i = 0; i != m; ++i) {
      if ((ma[i] = malloc(n*sizeof(int))) == NULL)
        return NULL;
      for (j = 0; j != n; ++j) {
        ma[i][j] = i*n + j;
      }
    }
  }

  return ma;
}

void obtn_matrix(int *** matrix,int * m,int * n)
{
    printf("Please enter the row number: ");
    fflush(stdin);
    scanf("%d",m);

    printf("Please enter the column number: ");
    fflush(stdin);
    scanf("%d",n);
    *matrix=create_matrix(*m,*n);
}

void free_matrix(int ** matrix,int m,int n)
{
    for(int i=0;i<m;i++)
    {
      if (matrix[i] == NULL) 
        /* matrix creation was aborted */
        break;
      free(matrix[i]);
    }

    free(matrix);
}

int main()
{
    int ** matrix,m,n;

    obtn_matrix(&matrix,&m,&n);
    if (matrix != NULL) {
      prnt_matrix(matrix,m,n);
      free_matrix(matrix,m,n);
    }
}

Compilation and execution :

pi@raspberrypi:~ $ gcc -pedantic -Wextra m.c
pi@raspberrypi:~ $ ./a.out
Please enter the row number: 3
Please enter the column number: 2
0 1 
2 3 
4 5 

Execution under valgrind

pi@raspberrypi:~ $ valgrind ./a.out
==10436== Memcheck, a memory error detector
==10436== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10436== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10436== Command: ./a.out
==10436== 
Please enter the row number: 3
Please enter the column number: 2
0 1 
2 3 
4 5 
==10436== 
==10436== HEAP SUMMARY:
==10436==     in use at exit: 0 bytes in 0 blocks
==10436==   total heap usage: 6 allocs, 6 frees, 2,084 bytes allocated
==10436== 
==10436== All heap blocks were freed -- no leaks are possible
==10436== 
==10436== For counts of detected and suppressed errors, rerun with: -v
==10436== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

Note it is also possible to just alloc one array of m*n int but in that case rather than to use m[i][j]you need to do m[i*n+j]

bruno
  • 31,755
  • 7
  • 21
  • 36
  • I like the approach, except `void obtn_matrix` does not need to take `matrix` as a parameter at all. Just make it `int **obtn_matrix (int *m, int *n)` and `return create_matrix (*m, *n);` (also allows you to avoid becoming a 3-star programmer). May also consider `if ((ma[i] = calloc(n, sizeof(int))) == NULL)` to insure all integers are initialized zero just in case OP omits initializing one before printing `:)` – David C. Rankin Feb 15 '19 at 22:28
  • @DavidC.Rankin I just followed the most I can the original code ;-) – bruno Feb 15 '19 at 22:30
  • Yep, I got it, I was just pointing those out. Like I said, I like the approach. – David C. Rankin Feb 15 '19 at 22:31
0

If you haven't fully wrapped your head around the basic problem you are having, the problem is this, C passes parameters to functions by value. What this means is that when a parameter is passed, the function receives a copy of the variable which it is free to change -- but any changes are lost when the function returns.

One caveat to this is when an allocated pointer is passed. While it is still passed by value and the pointer will have its very own and very different address from the original, the memory address it holds as its value will still point to the same address as it does in the caller. This is no different that assigning int a = 5; and then passing a as a parameter, the function receives a copy of the variable, but it still contains 5.

However, if you attempt to allocate or reallocate such that the address of the pointer is changed, then any changes made in the function will be lost on return and not visible in the caller.

How Do You Handle This?

You have two options (1) pass the address of the pointer, so any changes to the address can be assigned to the dereferenced pointer (i.e. the original pointer address), or (2) change the return type from void to the type of pointer that needs to be returned and return the newly allocated/reallocated pointer for assignment in the caller.

For example, in your case, you need not pass matrix as a parameter to obtn_matrix() at all. You can simply use option (2) above and change the return type of obtn_matrix() to int ** and return create_matrix (*m, *n); at the end, e.g.

int **create_matrix (int m, int n)
{
    int **matrix = malloc (m * sizeof *matrix);

    if (matrix == NULL) {
        perror ("malloc-matrix");
        return NULL;
    }

    for (int i = 0; i < m; i++)
        /* use calloc to initialize values zero */
        if ((matrix[i] = calloc (n, sizeof **matrix)) == NULL) {
            perror ("malloc-matrix[n]");
            return NULL;
        }

    return matrix;
}

/* use pointers as parameters so the updated values of m, n are
 * available back in the caller after the function returns. return
 * int** for assignment back in the caller. avoids becomming a
 * 3-Star Programmer (not a compliment). return NULL on failure.
 */
int **obtn_matrix (int *m, int *n)
{
    fputs ("enter number of rows: ", stdout);
    if (scanf ("%d", m) != 1) { /* validate ALL user-input */
        fputs ("error: invalid number of rows.\n", stderr);
        return NULL;
    }
    fputs ("enter number of cols: ", stdout);
    if (scanf ("%d", n) != 1) {
        fputs ("error: invalid number of cols.\n", stderr);
        return NULL;
    }

    return create_matrix (*m, *n);
}

Now putting it altogether, and making note that @bruno explains why you must pass the address of m, n (e.g. &m, &n) to obtn_matrix (option (1) above) so the updated values of m & n are available back in main() (the caller here), you could do something like:

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

int **create_matrix (int m, int n)
{
    int **matrix = malloc (m * sizeof *matrix);

    if (matrix == NULL) {
        perror ("malloc-matrix");
        return NULL;
    }

    for (int i = 0; i < m; i++)
        /* use calloc to initialize values zero */
        if ((matrix[i] = calloc (n, sizeof **matrix)) == NULL) {
            perror ("malloc-matrix[n]");
            return NULL;
        }

    return matrix;
}

/* use pointers as parameters so the updated values of m, n are
 * available back in the caller after the function returns. return
 * int** for assignment back in the caller. avoids becomming a
 * 3-Star Programmer (not a compliment). return NULL on failure.
 */
int **obtn_matrix (int *m, int *n)
{
    fputs ("enter number of rows: ", stdout);
    if (scanf ("%d", m) != 1) { /* validate ALL user-input */
        fputs ("error: invalid number of rows.\n", stderr);
        return NULL;
    }
    fputs ("enter number of cols: ", stdout);
    if (scanf ("%d", n) != 1) {
        fputs ("error: invalid number of cols.\n", stderr);
        return NULL;
    }

    return create_matrix (*m, *n);
}

void prnt_matrix (int **matrix, int m, int n)
{
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++)
            printf (" %4d", matrix[i][j]);
        putchar ('\n');
    }
}

void free_matrix (int **matrix, int m)
{
    for (int i = 0; i < m; i++)
        free (matrix[i]);       /* free integers */
    free (matrix);              /* free pointers */
}

int main (void) {

    int **matrix, m = 0, n = 0;

    if ((matrix = obtn_matrix (&m, &n)) == NULL)
        return 1;

    prnt_matrix (matrix, m, n);
    free_matrix (matrix, m);

    return 0;
}

Since you "didn't share the filler function", the code above simply zeros all integer values by allocating the integers for each row with calloc instead of malloc which is a good idea when working with simulated arrays so that if you inadvertently fail to initialize one of the integers, you don't SegFault when you attempt to traverse all elements to, e.g. prnt_matrix, etc...

Example Use/Output

$ ./bin/matrix_cr_obtn
enter number of rows: 5
enter number of cols: 3
    0    0    0
    0    0    0
    0    0    0
    0    0    0
    0    0    0

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/matrix_cr_obtn
==10251== Memcheck, a memory error detector
==10251== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10251== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==10251== Command: ./bin/matrix_cr_obtn
==10251==
enter number of rows: 5
enter number of cols: 3
    0    0    0
    0    0    0
    0    0    0
    0    0    0
    0    0    0
==10251==
==10251== HEAP SUMMARY:
==10251==     in use at exit: 0 bytes in 0 blocks
==10251==   total heap usage: 6 allocs, 6 frees, 100 bytes allocated
==10251==
==10251== All heap blocks were freed -- no leaks are possible
==10251==
==10251== For counts of detected and suppressed errors, rerun with: -v
==10251== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

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

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
-1

The code became convoluted with all the pointers, if you want to send 2D array to a function do this:

void obtn_matrix(int matrix[][n],int m,int n) // or equivalently: void obtn_matrix(int (*matrix)[n], int m, int n ) { ... }

[assuming you assign n a value before it is passed to the function (you can also give it a random value and then add space to it with malloc)]

Because int ** doesn't represent a 2D array - it would be an array of pointers to pointers.

C. Arnold
  • 77
  • 11