A pointer is just like an int, excpet holding memory address. When passed to a function, they are passed by value.
A simple implication where this matters is following:

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void array_init(int* array, int size) {
    int* from_malloc = (int*)malloc(size * sizeof(int));
    printf("Pointer from malloc: %p\n", (void*)from_malloc);
    if (from_malloc == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    array = from_malloc;
}
 
int main() {
    int* array_pointer = NULL;
    int size = 10;
    array_init(array_pointer, size);
 
    printf("Array pointer: %p\n", (void*)array_pointer);
 
    return 0;
}

It’s like sending in an int to a function and expecting it’s value to change in the caller scope. But it’s a commong gotcha.

But what does it matter if it’s ultimately the same memory location?
I said the pointer saying “here’s a memory location”, I don’t want you to change the contents of this memory location ( case of passing just int* ) but instead I want you to give me a new memory location instead. I’m not writing to the memory location, I’m writing to the pointer itself, That’s the difference.

o remediate is simple, pass the pointer by reference (as in int**)

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void array_init(int** array, int size) {
    int* from_malloc = (int*)malloc(size * sizeof(int));
    printf("Pointer from malloc: %p\n", (void*)from_malloc);
    if (from_malloc == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    *array = from_malloc;
}
 
int main() {
    int* array_pointer = NULL;
    int size = 10;
    array_init(&array_pointer, size);
 
    printf("Array pointer: %p\n", (void*)(array_pointer));
 
    free(array_pointer);
    array_pointer = NULL;
 
    return 0;
}