1

Can anyone explain me why i dont need -> int ***zd in the init function? Isn't this call by value and the intialization shouldnt stay when i want to print it? Or is a pointer automatically call by reference? I would love to understand how this exactly works, so if anyone can help me i would highly appreciate it!

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


void initZD(int **zd, int width, int height){
    for(int i = 0; i < width; i++){
        for(int j = 0; j < height; j++){
            zd[i][j] = rand() % 10;
        }
    }
    return;
}
void printZD(int **zd, int breite, int hoehe){
for(int i = 0; i < breite; i++){
    for(int j = 0; j < hoehe; j++){
        printf("%d\t",zd[i][j]);
    }
    printf("\n");
}
}

int main(){

int **zd = NULL;
int width, heigth;

printf("Width: ");
scanf("%d", &width);
printf("Heigth: ");
scanf("%d", &heigth);

//zd = realloc(*zd, breite*sizeof(int));
//zd = calloc(breite, sizeof(int));
zd = malloc(width*sizeof(int));


for(int i = 0; i < width; i++){
//zd[i] = realloc(*zd, hoehe*sizeof(int));
//zd[i] = calloc(hoehe, sizeof(int));
zd[i] = malloc(heigth*sizeof(int));
}

initZD(zd, width, heigth);

printZD(zd, width, heigth);


free(zd);

for(int x = 0; x < width; x++){
    free(zd[x]);
}

return 0;
}
vibecheck
  • 29
  • 3
  • 1
    `int **zd` is not a multi-dimensional array. It's a pointer to a one-dimensional array of pointers to individual one-dimensional arrays. – Andrew Henle Dec 29 '20 at 13:48
  • Thank you for your answer, that actually makes sense! – vibecheck Dec 29 '20 at 13:53
  • I still think i didnt got it 100 percent but this gives me a better idea of it for sure – vibecheck Dec 29 '20 at 13:54
  • 1
    This is a really good question-and-answer that explains pointers-to-pointers and true multi-dimensional arrays (and how to dynamically allocate them): [**Correctly allocating multi-dimensional arrays**](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Andrew Henle Dec 29 '20 at 13:57

4 Answers4

1

initzD does not need the address of zd because it does not change zd. It changes things that are pointed to via zd (specifically things that are pointed to by pointers that zd points to).

To change those things, it needs their addresses, and it has their addresses because they are pointed to by the pointers that zd points to. (Generally, the pointers only point to the first of some number of int, but the subscript operators in zd[i][j] do pointer arithmetic to calculate the addresses of following elements.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

The parameter doesn't need to be defined as int ***zd because you're not modifying the value of the pointer zd in the main function. You're just dereferencing it to modify values that it points to.

Had zd been uninitialized, then you would have to pass a pointer to it so that the function could do the memory allocation that is currently happening in main.

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

Your function initZD doesn't change the pointer int **zd which is passed by value. It not even changes the values of the pointer array where zd points to, but only the values where those pointers point to.

The pointer int **zd = NULL; in main is modified in main only because you allocate the memory there with

zd = malloc(width*sizeof(int));

You would have to pass the address of the pointer to initZD if you would want to allocate the memory inside the function.

Your code contains an error: In main you call free(zd) before you use it to free the other memory with free(zd[x])in the loop. This is undefined behavior.

Example where you would need to pass the pointer by reference, i.e. pass the address of the pointer:

void initZD(int ***zd, int width, int height){

    *zd = malloc(width*sizeof(int));

    for(int i = 0; i < width; i++){
        (*zd)[i] = malloc(heigth*sizeof(int));
    }
    for(int i = 0; i < width; i++){
        for(int j = 0; j < height; j++){
            (*zd)[i][j] = rand() % 10;
        }
    }
    return;
}

/* ... */

int main(){

    int **zd = NULL;
    int width, heigth;

    printf("Width: ");
    scanf("%d", &width);
    printf("Heigth: ");
    scanf("%d", &heigth);


    initZD(&zd, width, heigth);

    printZD(zd, width, heigth);

    for(int x = 0; x < width; x++){
        free(zd[x]);
    }

    /* NOTE you must not free this pointer before the loop above */
    free(zd);

    return 0;
}
Bodo
  • 9,287
  • 1
  • 13
  • 29
0

Call by value would mean that a copy of a variable is passed to the function whereas Call by reference would mean that the address of the variable itself is passed.

The implication of this is that changes which are made by operations in the called function would be reflected in the calling function. Arrays in C are passed by reference (by default) as you would pass the array variable to the function which is actually a pointer to the first element of the array.

When you write int ** it actually means you are pointing to an address which is pointing to an element (Hence the **). So if you want to access an integer element of the array you would need to de-reference twice. For the case here:

zd: It is the pointer to the 1st row of the 2D array

zd + i: It is the pointer to the ith row of the 2D array

*(zd + i)/ zd[i]: De-references zd + i to give the address of the base element of the ith row.

Similarly zd[i][j] adds j to the base of the ith row and dereferences it to give the value of the element at ith row and jth column.

Here rules of addition are governed by pointer arithmetic, meaning since an integer occupies 4 bytes of data and if memory is byte organized a pointer to an integer when incremented by 1 would point 4 units ahead of what it pointed to earlier.