-1

I'm trying to generate a matrix of some arbitrary dimensions. I can do it just fine by calling scanf in main and then assigning matrix elements on a row by row basis, but trying to do it in a single function, outside of main, (and only if scanf() is called outside of main) gives me a segfault error:

int **genmat(int nrow, int ncol){
    int i,j;
    int **mat = (int**) malloc(sizeof(int)*ncol*nrow);  
    char rowbuff[16];
    for(i=0; i < nrow; i++){
        INPUT: scanf("%[^\n]%*c",rowbuff);
        if(strlen(rowbuff) != ncol){
            printf("Error: Input must be string of length %d\n", ncol);
            goto INPUT;
        }
        else{
            for(j=0; j < ncol; j++){
                if(rowbuff[j] == '1'){
                    mat[i][j] = 1;
                }
                else{
                    mat[i][j] = 0;
                }
            }
        }
    }
    return(mat);
}

The following works just fine:

int *genrow(int ncol, char *rowbuff){
    int i;
    int *row = malloc(sizeof(int)*ncol);
    for(i=0;i<ncol;i++){
        row[i] = rowbuff[i]%2;
    }
    return(row);
}

with the following in my main function to call genrow() for each row of the matrix:

    for(i=0; i < row; i++){
        INPUT: scanf("%[^\n]%*c",rowbuff);
        if(strlen(rowbuff) != col){
            printf("Error: Input must be string of length %d\n", col);
            goto INPUT;
        }
        else{
            int *newrow = genrow(col, rowbuff);
            for(j=0; j < col; j++){
                matrix[i][j] = newrow[j];
            }
            free(newrow);
            newrow = NULL;
        }
    }

Why is the behavior different in these two contexts?

Chib
  • 11
  • 4
  • 3
    `int **mat = (int**) malloc(sizeof(int)*ncol*nrow);` this isn't doing what you think it is. `mat[i]` results in a `int*` (pointer to `int`), which remains indeterminate. And, stock comment, [stop casting memory allocation functions in C program](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – WhozCraig Dec 07 '16 at 09:36
  • 2
    And please, never use goto and label... – Fefux Dec 07 '16 at 09:38
  • 2
    `int **mat = (int**) malloc(sizeof(int)*ncol*nrow);` should be `int (*mat)[ncol] = malloc(nrow * sizeof *mat);`. – mch Dec 07 '16 at 09:41
  • Anybody care to share why they downvoted the question? – Chib Dec 07 '16 at 09:55
  • probably because it is not a minimal, complete and compilable program. – mch Dec 07 '16 at 09:57

2 Answers2

0

Dynamically allocated 2D arrays are unfortunately burdensome and ugly in C. To properly allocate one, it is very important that you do so with a single call to malloc, just as you tried to do. Otherwise it won't be a 2D array, but instead some segmented, slow look-up table.

However, the result of that malloc call will be a pointer to a 2D array, not a pointer-to-pointer. In fact, pointer-pointers have nothing to do with 2D arrays whatsoever - this is a widespread but incorrect belief.

What you should have done is this:

int (*mat)[nrow][ncol] = malloc( sizeof(int[nrow][ncol] );

This is an array pointer to a 2D array. This syntax is already a bit burdensome, but to make things worse, it is not easy to pass this array pointer back to main, because it is a local pointer variable. So you would need to use a pointer to an array pointer... and there's no pretty way to do that. It goes like this:

void genmat (size_t nrow, size_t ncol, int (**mat)[nrow][ncol] )
{
  *mat = malloc( sizeof(int[nrow][ncol]) );

To ease usage a bit, you can create a temporary pointer to rows, which doesn't require multiple levels of indirection and is therefore much easier to work with:

  int (*matrix)[ncol] = *mat[0]; // in the pointed-at 2D array, point at first row

  for(size_t r=0; r<nrow; r++)  // whatever you want to do with this matrix:
  {
    for(size_t c=0; c<ncol; c++)
    {
      matrix[r][c] = 1;  // much more convenient syntax than (**mat)[r][c]
    }
  }

From main, you'll have to call the code like this:

size_t row = 3;
size_t col = 4;

int (*mat)[row][col];
genmat(row, col, &mat);

Example:

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


void genmat (size_t nrow, size_t ncol, int (**mat)[nrow][ncol] )
{
  *mat = malloc( sizeof(int[nrow][ncol]) );
  int (*matrix)[ncol] = *mat[0];

  for(size_t r=0; r<nrow; r++)
  {
    for(size_t c=0; c<ncol; c++)
    {
      matrix[r][c] = 1;
    }
  }
}

void printmat (size_t nrow, size_t ncol, int mat[nrow][ncol])
{
  for(size_t r=0; r<nrow; r++)
  {
    for(size_t c=0; c<ncol; c++)
    {
      printf("%d ", mat[r][c]);
    }
    printf("\n");
  }
}

int main (void)
{
  size_t row = 3;
  size_t col = 4;

  int (*mat)[row][col];
  genmat(row, col, &mat);

  printmat(row, col, *mat);

  free(mat);
  return 0;
} 

Please note that real code needs to address the case where malloc returns NULL.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • That seems to have gotten it. I think that I more or less understand what you did with the genmat (correct me if I'm wrong), which is that you created an array with two indices in main, told genmat where it lived and then just did the assignments without telling genmat to return a pointer (since `**mat[i][j]` would effectively be the object pointing to a pointer instead of some actual *thing* in the memory with `i*j` entries). I *am* unfamiliar with how declarations with parentheses work though. Can you link me to any good material on declarations? – Chib Dec 07 '16 at 21:35
  • @Chib Here is a more detailed explanation: http://stackoverflow.com/questions/32050256/function-to-dynamically-allocate-matrix/32050859#32050859 – Lundin Dec 08 '16 at 07:32
-1

I assume the problems is withint **mat = (int**) malloc(sizeof(int)*ncol*nrow); You are trying to allocate the a 2D array right? But this isn't the correct method to allocate the memory. You can't allocate the whole chunk of memory one short. What you should be doing here is, allocate the memory for all the rows(basically pointer to store the column address) and then for columns

int **mat= (int **)malloc(nrow * sizeof(int *));
for (i=0; i<nrow; i++)
     mat[i] = (int *)malloc(ncol * sizeof(int));

Refer this link for more info http://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/

pdsr
  • 1
  • 1
  • This isn't a 2D array, it's a look-up table. The OP is using a 2D array. Of course the correct method is to allocate the memory as one single chunk, otherwise it wouldn't be an array. – Lundin Dec 07 '16 at 09:56
  • Btw that site is awful, please stop reading it and don't recommend it to others. – Lundin Dec 07 '16 at 10:14
  • plz suggest an alernative for this geeksforgeeks – pdsr Dec 07 '16 at 10:55
  • Also, by the end of the day, it is still an 2D array rt :P – pdsr Dec 07 '16 at 10:56
  • Alternative: http://stackoverflow.com/. Also, just because something looks like a 2D array doesn't make it one. You can't dress up your pigs as cows and then sell the meat as cow meat, no matter how cow-like the pigs looked. – Lundin Dec 07 '16 at 11:50