These three lines declare memory for three integers, and initialize the integers. Should you do this outside of a function, you can happily take the address of these variables and store them in your array.
int a = 1;
int b = 2;
int c = 3;
However, should the above three variables be declared in a function (on the stack) and you take the address of them, and store those addresses somewhere, you have created a (potential) dangling pointer problem.
This line allocates enough memory to hold three pointers to int (12 or 24 bytes),
int **array_a = (int **) malloc (sizeof (int *) * 3);
Now you store the address of the earlier defined variables a,b,c into the array_a[],
array_a[0] = &a;
array_a[1] = &b;
array_a[2] = &c;
Which is either perfectly harmless, or very dangerous, depending upon where a,b,c were declared, for example,
int** youfun()
{
int a = 1;
int b = 2;
int c = 3;
int **array_a = (int **) malloc (sizeof (int *) * 3);
array_a[0] = &a;
array_a[1] = &b;
array_a[2] = &c;
return(array_a); //very bad!
}
int a = 1;
int b = 2;
int c = 3;
int** mefun()
{
int **array_a = (int **) malloc (sizeof (int *) * 3);
array_a[0] = &a;
array_a[1] = &b;
array_a[2] = &c;
return(array_a); //safe, but strange
}
Declaring and allocating space for array_b[], and reserving a single memory location is similar to declaring and array of one pointer to int,
int **array_b = (int **) malloc (sizeof (int *) * 1);
The following assignment places the contents of array_a[0] (which is the address of variable a, &a, from above), and is only as dangerous/innocuous as having the &a stored in array_a[0],
array_b[0] = array_a[0];
Freeing the array_a is harmless, because nothing is stored in array_a which might 'leak', and does not affect array_b[0], as that contains the address of a, &a,
free(array_a);
Suppose you did the following instead,
int **array_a = (int **) malloc (sizeof (int *) * 100);
int ndx;
for(ndx=0; ndx<100; ++ndx)
array_a[ndx] = malloc( sizeof(int) );
You would now have allocated 100+1 memory locations, which is still fine.
Then suppose you allocated array_b will enough space to hold all of array_a[],
int **array_b = (int **) malloc (sizeof (int *) * 100);
int ndx;
for(ndx=0; ndx<100; ++ndx)
array_b[ndx] = malloc( sizeof(int) );
This would leak memory (pointed at by array_b), plus the memory pointed at by each array_b[ndx], for a total of 100+1 memory location leaks,
array_b = array_a; //discarded memory references at array_b[0..99], and array_b
Now suppose you did both of these,
array_b = array_a; //you just discarded the memory references at array_b[0..99] and array_b
free(array_a); //you just discarded array_a[0..99]
The above would leak all memory pointed to by array_b, array_b[0..99] and all memory at array_a[0..99], as you only copied array_a's address, not the addresses at array_a[0..99].
Here is how you would copy the memory allocated at array_a[0..99],
for(ndx=0; ndx<100; ++ndx)
array_b[ndx] = array_a[ndx];