-1

Basically i understand pointers. But when it comes to dynamic allocation for matrices which also involve pointers, i'm getting lost in the process. I wanna know how can i translate this segment of code in order to understand it.

(*a)[i] = (int*)malloc((*m) * sizeof(int));

The function for reading the matrix looks like this:

void reading(int *n, int *m, int ***a) {
    int i, j;
    printf("n=");
    scanf("%d", &*n);
    printf("m=");
    scanf("%d", &*m);
    (*a) = (int**)malloc((*n) * sizeof(int*));   
    for (i = 0; i < *n; i++)                     
        (*a)[i] = (int*)malloc((*m) * sizeof(int)); 
    for (i = 0; i < *n; i++) {
        for (j = 0; j < *m; j++) {
            printf("a[%d][%d]=", i, j);
            scanf("%d", &(*a)[i][j]);
        }
    }
}

And also what is the meaning of ***a in the declaration. I was told at college that te first asterisk stands for dynamic allocation and the other two's from the fact that is a matrix involved. For vectors dynamic allocation is **v and so on... but i can't naturally explain it in my mind in order understand what is happening in it.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • 1
    I's difficult to precisely say what is going on without knowing what `a` and `n` are. – Marco Bonelli Jan 14 '20 at 20:18
  • a is a matrix declared int ***a – Vlad Gabriel Jan 14 '20 at 20:18
  • Related, at least: https://stackoverflow.com/q/89056/10077 – Fred Larson Jan 14 '20 at 20:18
  • What `***a` are you talking about? – lxop Jan 14 '20 at 20:18
  • *n points to the number of lines and there is also an *m pointing to the number of columns – Vlad Gabriel Jan 14 '20 at 20:19
  • 1
    Sounds like you need to post a little more of the code around this line to provide context – lxop Jan 14 '20 at 20:19
  • 6
    `scanf("%d", &*n);` really lol – Marco Bonelli Jan 14 '20 at 20:25
  • In main i call the function like : reading ( &n , &m , & a) ; – Vlad Gabriel Jan 14 '20 at 20:26
  • 5
    In C any `***` is considered bad luck by many programmers. – Yunnosch Jan 14 '20 at 20:26
  • You could simplify by returning a pointer: `int ** reading(int *n, int *m);` Or, even create a struct: `struct matrix {int rows; int cols; int **data;};` and then pass that: `void reading(matrix *m);` – 001 Jan 14 '20 at 20:37
  • 1
    Forget what you were taught about what pointers "mean". Learn what they actually are: A simple variable, like `int a = 5`, is just a small piece of memory (probably 4 or 8 bytes) that contains the value 5. A pointer `int *p = &a` is just a similar piece of memory, but it contains a *memory address* of some variable instead of a value. `a = 5` means "put the value 5 into the memory labeled `a`, `*p = 5` means put the value 5 into the variable at the address stored in `p`. So, `**p` is just a memory address of a memory address, and `***p` is an address of an address of an address, etc. – Lee Daniel Crocker Jan 14 '20 at 20:52
  • A lot of schools apparently teach pointers the wrong way. A pointer is just a variable that contains the memory address of another variable. It "points to" another variable in that it contains the memory address of that other variable. You can also take the addresses of pointers, which is why you see `**` here. – S.S. Anne Jan 14 '20 at 22:29

4 Answers4

1

First let me answer your question about this specific line:

(*a)[i] = (int*)malloc((*m) * sizeof(int));

What this is doing is allocating an array of exactly *m integers and saving a pointer to it into the array *a of pointers to int, which was previously allocated as:

(*a) = (int**)malloc((*n) * sizeof(int*));

Now, if it still isn't clear what is going on, re-writing the code in a more meaningful way will help. To make things easier, you can use temporary variables to work, and assign the values to the pointers passed as arguments only at the end of the function. Using more meaningful names also helps a lot.

void read_matrix(int *rows, int *columns, int ***matrix) {
    int i, j, r, c;
    int **mat;

    printf("n = ");
    scanf("%d", &r);

    printf("m = ");
    scanf("%d", &c);

    // Allocate space for a matrix (i.e. an array of r integer pointers).
    mat = malloc(r * sizeof(int*));

    // Allocate space for each row of the matrix (i.e. r arrays of c integers).
    for (i = 0; i < r; i++)
        mat[i] = malloc(c * sizeof(int));

    for (i = 0; i < r; i++) {
        for (j = 0; j < c; j++) {
            printf("a[%d][%d] = ", i, j);
            scanf("%d", &mat[i][j]);
        }
    }

    *rows = r;
    *columns = c;
    *matrix = mat;
}

Since we now moved the assignment of the values to the arguments at the end of the function, we got rid of all the annoying pointer dereference operators (*), and the code looks way cleaner.

You can see that what previously was:

(*a)[i] = (int*)malloc((*m) * sizeof(int));

now became:

mat[i] = malloc(c * sizeof(int));

Which is much easier to understand. This is allocating space for an array (a row of the matrix) holding c integers.

What previously was:

(*a) = (int**)malloc((*n) * sizeof(int*));

now became:

mat = malloc(r * sizeof(int*));

This is allocating an array of r integer pointers (which means a matrix of r rows, if each pointer points to a row).

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • I would use `mat = malloc(r * sizeof(*mat));` as it may be more clear and it won't fail if the type of `mat` ever changes. Same for `mat[i] = malloc(c * sizeof(int));`, which I would replace with `mat[i] = malloc(c * sizeof(*mat[i]));` – S.S. Anne Jan 14 '20 at 22:31
0

You don't show how this function is called, but presumably it looks something like this:

int n, m;
int **matrix;
reading(&n, &m, &matrix);

So in this context, matrix is defined as a pointer-to-pointer. It can hold the address of the first element of an array of int *, each of which can hold the address of the first element of an array of int.

When &matrix is then passed to this function, you have a pointer-to-pointer-to-pointer, which is what the argument a of reading is. In this context, a contains a pointer to a single int **, specifically matrix in the calling function. By dereferecing a in reading, you're actually accessing matrix in the calling function.

So now getting to this line:

(*a) = (int**)malloc((*n) * sizeof(int*));

This allocates space for an array of *n int * and assigns that to *a, (i.e. matrix in the calling funtion. So now you have an array of int *. Now for this:

for (i = 0; i < *n; i++)                     
    (*a)[i] = (int*)malloc((*m) * sizeof(int)); 

This loops through the elements of the int * array and assigns to each one a pointer to a memory block big enough for *m int.

So you now effectively have a 2D array of int. Note however that this is not the same as an actual 2D array of int which would be declared as int arr[n][m].

dbush
  • 205,898
  • 23
  • 218
  • 273
0

First, you are doing too many different things in a single function, which is making it a bit messy. I suggest that you separate out the logic to get the matrix size from the logic to create the matrix:

void get_size(int *n, int *m) {
    printf("n=");
    scanf("%d", n);
    printf("m=");
    scanf("%d", m);
}

int **create_matrix(int n, int m) {
    int **matrix = malloc(n * sizeof(int*));   
    for (int i = 0; i < n; i++)                     
        matrix[i] = malloc(m * sizeof(int)); 
    return matrix;
}

void fill_matrix(int **matrix, int n, int m) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            printf("a[%d][%d]=", i, j);
            scanf("%d", [i][j]);
        }
    }
}

From here it is a lot easier to see what is going on, with fewer *s and &s.

Your matrix is implemented as an array of arrays, so int **matrix = malloc(n * sizeof(int*)); allocates memory for the outer array, while matrix[i] = malloc(m * sizeof(int)); allocates memory for each of the inner arrays.

lxop
  • 7,596
  • 3
  • 27
  • 42
0

int ***a declares a to be a pointer to a pointer to pointer to an int. The caller is required to have their own int ** and to pass its address to this function. For example, the caller might define int **x; and pass &x to this function for the parameter a. I will use x to refer to the caller’s int **.

(*a) = (int**)malloc((*n) * sizeof(int*)); sets the caller‘s pointer (x) to point to space for *n pointers to int. This is preparation for fabricating a matrix of *n rows—memory will be allocated for each row, and we will have a pointer to that memory, so we need n pointers.

Then these lines:

for (i = 0; i < *n; i++)                     
    (*a)[i] = (int*)malloc((*m) * sizeof(int));

allocate memory for *n rows. The second line allocates memory for an array of m int and sets x[i] to point to the first element of that memory. Note that since a is an int ***, *a is an int **, and (*a)[i] is an int *. Thus, *a points to an array of int * elements.

Finally, these lines:

for (i = 0; i < *n; i++) {
    for (j = 0; j < *m; j++) {
        printf("a[%d][%d]=", i, j);
        scanf("%d", &(*a)[i][j]);
    }
}

set each element of the *n by *m array: For each element x[i][j] (referred to as (*a)[i][j], it passes the address of the element (&(*a)[i][j]) to scanf to be set from the input stream.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • so that (int**) after the "=" and before malloc , it's regarding the caller ? – Vlad Gabriel Jan 14 '20 at 20:47
  • @VladGabriel: `(int **)` is a cast. It converts its operand to be of type `int **`. In C, it is not needed with `malloc`. `malloc` returns a pointer of type `void *`, and that will be automatically converted to the type of whatever pointer it is assigned to. In C++, it would be required, because C++ does not do the automatic conversion. – Eric Postpischil Jan 14 '20 at 23:52