0

So what I'm trying to do is create a two dimensional array as a matrix using pointers. I put a double pointer into my CreateMatrix function, along with rows/cols, and I dynamically allocate the arrays into them. I filled 10 into all of them to test, and it shows they're all allocated. However in main, when I try to access an arbitrary value of the 2D array, I segfault. Regardless of if it occurs in a function, shouldn't dynamically allocated be stored onto the heap, thus it should work?

void CreateMatrix(int ** matrix, int row, int col){

 int i, j;
 matrix = (int**)malloc(row* sizeof(int*));
 for (i = 0; i < row; i++) {
    matrix[i] = (int*)malloc(col* sizeof(int));
    for (j = 0 ; j < col; j++) {
        matrix[i][j] = 10;
        printf("i: %d, j: %d\n", i,j);
        printf("%d",matrix[i][j]);
    }
 }
}

In main:

    int ** matrix1;
    int m1row, m1col = 5;

    CreateMatrix(matrix1, m1row, m1col);
    fprintf(stdout,"Testing if got out");
    fflush(stdout);
    printf("This should be 10 : %d",matrix1[0][0]); //definitely segfaults here

Also, a side question; I was told that when passing 'by reference' in C, not only do you need pointers in the function definition, but an ampersand on the variable for the function call. Something like if I want to pass the matrix a by reference : CreateMatrix(&a, b, c); instead of CreateMatrix(a, b, c);. If I use an &, I get an incompatibility error saying the argument is a triple pointer,when I need a double pointer. (Which in theory makes sense, since you're passing the location of the double pointer). Then is & only used for non pointer variables?

lightalchemist
  • 10,031
  • 4
  • 47
  • 55
Duke K
  • 11
  • 3
  • `matrix` assigned in `CreateMatrix()` does not affect `matrix1` in `main()`. – chux - Reinstate Monica Sep 18 '18 at 05:37
  • Also: what is the value of `m1row` when code calls `CreateMatrix(matrix1, m1row, m1col)`? – chux - Reinstate Monica Sep 18 '18 at 05:39
  • 1
    Ahhh I quickly wrote that portion up. I originally had it so that I was taking in values of m1row and m1col. I thought that was correct formatting to be honest. Then does int m1row, m1col; m1row, m1col = 5; not work too? – Duke K Sep 18 '18 at 05:45
  • 1
    Look at the `malloc` function signature. It creates memory from nothing. It returns a pointer to that memory. It doesn't need and does not receive any pointer from the outside. Now look at your function signature. It creates a matrix from nothing. Why is your function signature so drastically different from that of `malloc`? Is it justified? Why does your function receive a pointer from outside? Does it need that pointer? – n. m. could be an AI Sep 18 '18 at 05:47
  • `m1row, m1col = 5;` does not affect `m1row`. For clarity of problem presentation, better to use different values anyways. Suggest `int m1row = 4; int m1col = 5;` or the like. – chux - Reinstate Monica Sep 18 '18 at 05:50
  • I could have definitely just created a function that takes in rows & cols, and returns an int ** matrix, especially since I just put in an empty int **. I think I just got used it doing this way. Is it considered to be poor style?@n.m. – Duke K Sep 18 '18 at 05:56
  • Ahh ,okay I see. So I can initialize types onto multiple variables, but not values for multiple variables. Thanks! – Duke K Sep 18 '18 at 05:58
  • "I think I just got used it doing this way". I rather doubt that. Doing it "this way" involves the `&` operator at the call site. And it involves `*argument=...` or equivalent in the function. And it involves counting asterisks in types. You are doing none of that. – n. m. could be an AI Sep 18 '18 at 07:25
  • I originally came from C++ so it was relatively easy just sticking an &var in the function definition and that'd be that. I'm definitely learning that C is much more reliant on knowledge of how to correctly manage pointers. – Duke K Sep 18 '18 at 07:49

3 Answers3

1

matrix in CreateMatrix is a copy of the pointer matrix1. When you assign to matrix in that function, matrix1 in main is unaffected.

int** CreateMatrix(int row, int col){

 int i, j;
 int **matrix = (int**)malloc(row* sizeof(int*));
 for (i = 0; i < row; i++) {
    matrix[i] = (int*)malloc(col* sizeof(int));
    for (j = 0 ; j < col; j++) {
        matrix[i][j] = 10;
        printf("i: %d, j: %d\n", i,j);
        printf("%d",matrix[i][j]);
    }
 }
 return matrix;
}

int main() {

// ...
   int **matrix1 = CreateMatrix(row, col);

// ...

return 0;
}

If you want to keep a void return type, then take a pointer to a int** pointer and pass address of that pointer in main.

void CreateMatrix(int ***matrix, int row, int col){

    int i, j;
    *matrix = (int**)malloc(row* sizeof(int*));
    for (i = 0; i < row; i++) {
        (*matrix)[i] = (int*)malloc(col* sizeof(int));
        for (j = 0 ; j < col; j++) {
            (*matrix)[i][j] = 10;
            printf("i: %d, j: %d\n", i,j);
            printf("%d", (*matrix)[i][j]);
        }
    }
}


int main() {

    int **matrix1;
    CreateMatrix(&matrix1, 10, 10);
    printf("This should be 10 : %d",matrix1[0][0]);

    return 0;
}

Variables reside in memory. A pointer store the memory address of a variable with a specific type. The ampersand & gives the memory address of a variable.

Here, & returns the address to the int** pointer, matrix1, which is then passed to CreateMatrix. Notice that the argument matrix of CreateMatrix has been changed to be a pointer to a int** pointer.

Dereferencing a pointer gets you the object that the pointer is pointing to. Here, when we assign to *matrix, what we are doing is essentially using the memory address of matrix1 (which was passed via matrix) to get matrix1 in CreateMatrix, and assign the start of the block of memory allocated by malloc to matrix1.

In your original code, matrix in CreateMatrix is a copy of the value of the pointer matrix1 (probably garbage because it is not initialized).

When you assign to matrix, you are not modifying matrix1, you are merely modifying a local variable.

lightalchemist
  • 10,031
  • 4
  • 47
  • 55
1

There are few issues with the code you mention. firstly m1row value is not defined, so it may consider some garbage value, so assign the value like

int m1row = 5, m1col = 5;

Secondly, you are passing matrix1 to CreateMatrix() function & creating dynamic array in the CreateMatrix() function but not returning the dynamically allocated address to main() function which causes segmentation fault when accessing

printf("This should be 10 : %d",matrix1[0][0]);

Also casting the malloc() result is not required as pointed here.

Sample Code

int** CreateMatrix(int row, int col){
  int i, j;
  int **matrix = malloc(row* sizeof(*matrix)); /* no need to cast the malloc result */
  for (i = 0; i < row; i++) {
      matrix[i] = malloc(col* sizeof(**matrix));
      for (j = 0 ; j < col; j++) {
        matrix[i][j] = 10;
        #if 0
        printf("i: %d, j: %d\n", i,j);
        printf("%d \n",matrix[i][j]);
        #endif
      }
  }
  return matrix; /* return the dynamic array */
}
int main(void){
    int m1row = 5, m1col = 5;
    int **matrix1 = CreateMatrix(m1row, m1col);
    printf("\nThis should be 10 : %d\n",matrix1[0][0]); 
    return 0;
}
Achal
  • 11,821
  • 2
  • 15
  • 37
0

The matrix argument of the CreateMatrix function is allocated on the stack of that function, which means that it is local to it. When you use malloc to allocate memory, the local variable matrix is pointing to that memory. The matrix variable in main does not get updated by that, this is because arguments are by default passed by value.

In order to make it work, you would have to pass the address of the matrix variable (using the & operator) from main to CreateMatrix, i.e. its signature should have int *** matrix. Inside the function you would have to update it by using the * operator. Like so:

*matrix = (int **) malloc (row * sizeof(int *))

Another option is instead to return the allocated memory from the CreateMatrix function.

Abhinav Upadhyay
  • 2,477
  • 20
  • 32