In the piece of code below, what does the line: p[1]=p[0] entail? Does that mean that the data allocated to the p[0] is in the same address as p[1]? Also how is it even possible to utilize indexing when no memory has been allocated to each specific index of p? The code:
int i,j;
int **p = (int **)malloc(2 * sizeof(int *));
p[0] = (int *)malloc(2 * sizeof(int));
p[1] = p[0];
for(i = 0; i < 2; i++)
for(j = 0; j < 2; j++)
p[i][j] = i + j;
printf("%d",p[0][0]);
return 0;
I thank you in advance for sharing with me a part of your wisdom!
This looks like it's trying to create a 2D matrix that is 2x2 in size (as p[row][column]). However, only 1 row is actually allocated. Then p[1] = p[0]; means row 1 is pointing back to row 0. Essentially it's a 2x2 matrix where the both rows are identically the same.
This allocates two "pointers to a pointers" which allow you to use the row index p[row], but the rows still point to garbage at this point:
int **p = (int **)malloc(2 * sizeof(int *));
Then the actual row memory is allocated that allows you to use the column index p[row][column]:
p[0] = (int *)malloc(2 * sizeof(int));
The correct way to achieve an actual 2x2 matrix would be to allocate space for all 4 serialized values first like this:
p[0] = (int *)malloc(4 * sizeof(int));
Then have row 1 point to the 3rd element:
p[1]= &p[0][2];
This is not to say you should write your code this way (there are far better ways), it is just an explanation of the fishy code you provided.
Q1 and 2 :
p is defined as :
int **p = (int **)malloc(2 * sizeof(int *));
Then, p[0] and p[1] are int * so when you do p[1] = p[0], you are assigning to p[1] the same value than p[0] Careful, the pointers are assagned, not the value that they are pointing to, so if after this you change p[1][i], p[0][i] will also be changed
To better present this, I will try to draw the memory:
Before the assignment:
variable | Memory address
p[0]----points to----> 0xXXXX (x an arbitrary number)
p[1]----points to----> 0xYYYY (y an arbitrary number)
After the assignment:
p[0]----points to----> 0xXXXX
p[1]----points to----> 0xXXXX
Q3:
As p[1]=p[0] and you previously allocated p[0] , then p[1] points to the zone in memory that was alocated with :
p[0] = (int *)malloc(2 * sizeof(int));
Therefore there is not any issue. If you remove the p[1] = p[0] line, it should thow a segfault.
To create a 2x2 matrix, you should do:
int i;
int **p = malloc(2 * sizeof(int *));
for (i=0; i<2; i++)
p[i] = malloc(2 * sizeof(int));
Related
I am making a Rubik's Cube that can rotate it's rows and columns. My struct is this:
typedef struct
{
int **cell;
}face;
In this part I am allocating memory for cells. (X is user input)
face faceOne; //There are six of this faces
faceOne.cell = (int **)malloc(x * sizeof(int));
for(int i = 0; i < x; i++)
faceOne.cell[i] = (int *)malloc(x * sizeof(int));
Then I am filling these cells:
for (int i = 0; i < x; i++)
for (int j = 0; j < x; j++)
{
faceOne.cell[i][j] = 0;
printf("%d\n", i);
}
If x is bigger than 3, program crashes with Segmentation fault. How can i solve this?
faceOne.cell = (int **)malloc(x * sizeof(int));
would be
faceOne.cell = malloc(x * sizeof(int*));
Otherwise you are allocating memory for x int variables and then storing int* variable on it. In case sizeof(int) = sizeof(int*) then it wont be any problem but on the system where this is not the case you have earned yoruself a undefined behavior.
To get rid of all this ** while using malloc the best would be to do this
faceOne.cell = malloc(x * sizeof *faceOne.cell);
Similarly,
faceOne.cell[i] = malloc(x * sizeof *faceOne.cell[i] );
And also don't cast the return value of malloc. This suppresses many warning generated by compiler and is unnecessary.
Apart from all this, have you ever wondered what would happen if malloc did fail and returned NULL? Then you are working on a NULL and dereferencing it(Undefined behavior). Check the return value of malloc, in case it returns NULL handle the error and move on.
You want here
faceOne.cell = (int **)malloc(x * sizeof(int));
to allocate x pointers to int, so this should be
faceOne.cell = malloc(x * sizeof(int *));
It seems on your system the size of a pointer (to int) is bigger than the size of an int ...
Also in C don't cast malloc() (see this)
I know there are very similar questions, but I've read them and I don't understand what is going wrong or what exactly I haven't understood about pointers to pointers.
My teacher is using a "learning by doing" approach to pointers and it has left me with approximately 100 questions. Sometimes I just change things around until it compiles, but it really isn't becoming any more clear to me, so if someone could help clarify a few things, I'd really appreciate it.
struct Matrix {
int rows; // number of rows
int cols; // number of columns
double **data;
};
typedef struct Matrix Matrix;
The pointer to a pointer, is something like this, right?
double *row1 = (double *) malloc (n_cols*sizeof(double));
double *row2 = (double *) malloc (n_cols*sizeof(double));
double *row3 = (double *) malloc (n_cols*sizeof(double));
double *data[] = { row1, row2, row3};
Data is pointing to the row number which is pointing to the doubles in the rows.
Now I am supposed to make a constructor function that makes a Matrix with a 0 in every position returns a pointer to a Matrix.
Matrix *make_matrix(int n_rows, int n_cols) {
Matrix *m = xmalloc(sizeof(Matrix));
m->rows = n_rows;
m->cols = n_cols;
double **rows_and_columns = xmalloc(n_rows * n_cols * sizeof(double));
memset(rows_and_columns, 0, m->rows * m->cols * sizeof(double));
m->data = *rows_and_columns;
return m;
}
So I made a pointer for the matrix, then I assigned the values for the rows and columns. Then I got confused (although I am not sure how confused, because this part compiles). I made a pointer to pointer for the last element of the struct Matrix (**data). Since **rows_and_columns has to hold the rows and columns, I allocated xmalloc(n_rows * n_cols * sizeof(double)) memory to it. I then set the whole thing to 0 and assign it to data. I think this m->data = rows_and_columns; says that m points to data and since data is a pointer and rows_and_columns is a pointer, we'll align their addresses, so m->data will also point to a bunch of 0s? Or is this wrong? And I am returning m, because the output is Matrix * and the m will get the * upon output, correct?
The next step is to copy a matrix, at which point I got even more lost.
Matrix *copy_matrix(double *data, int n_rows, int n_cols) {
Matrix *m = make_matrix(n_rows, n_cols);
double *row = (double *) malloc (n_cols*sizeof(double));
int i = 0;
for (int j = 0; j < n_rows; j++) {
for (; i < n_cols; i++) {
row = (double *) malloc (n_cols*sizeof(double));
row [i] = data[i + j*n_cols];
}
m->data [j] = *row [i];
}
free(row);
return m;
}
So we are returning a pointer to a Matrix again. The input is now a pointer to double values, which I am assuming are the rows. First, I made a matrix. Then I made a pointer for a row with a n columns worth of memory (double *) malloc (n_cols*sizeof(double)).
This is where I got super confused. I was imagining **data the whole time as something like above (double *data[] = { row1, row2, row3};). So, I wanted to copy each row of *data into *row, then save that as an entry in **data, like data[0] = row0, but something isn't clicking with the pointers, because I am not allowed to assign m->data [j] = row [i];, because I'm assigning incompatible types by assigning double * from type double?
xmalloc() returns a void * pointer to single block of memory.
What you need is one block of pointers, serving as an conceptual table header row, holding pointers to other memory blocks which themselves contain the actual doubles.
double **columns -> [double *col0] [double *col1] [double *col2] ...
| | |
V V V
[double col_val0] [double col_val0] ...
[double col_val1] [double col_val1]
[double col_val2] [double col_val2]
... ...
A matrix allocation could look like this:
// Allocate the double pointer array:
double **matrix_rows = xmalloc(sizeof(double *) * column_count);
// Iterate over each double-pointer in the double-pointer-array allocated above.
for(int i = 0; i < column_count; i++) {
// Allocate a new double-array, and let current double-pointer point to it:
matrix_rows[i] = malloc(sizeof(double) * row_count);
// Initialize matrix cell, iterating over allocated values.
for(int j = 0; j < row_count; j++) {
// Accessing the i'th col and j'th cell.
matrix_rows[i][j] = 0.0;
}
}
A possible implementation of a matrix copy function could be done by iteratively copying individial cells. One way to do this is using a loop composition.
for(int col = 0; col < col_count; col++) {
for(int row = 0; row < row_count; row++) {
destination_matrix[col][row] = source_matrix[col][row];
}
}
To give some intuition where an n-pointer indirection could be used:
n = 1: Strings, an array of characters.
n = 2: Paragraph, holding lines of strings.
n = 3: An article, holding a list of paragraphs.
Please be aware of using two indirections is usually not something you want. It is usually more efficient to store data in a linear fashion and compute linear indices out of two-compoment vectors and the other way around, especially in the case of this matrix example.
If you want to represent a matrix as an array of pointers to rows you need to allocate memory both for the rows and for the array of pointers to rows. It is simpler to allocate the rows consecutively as a single block.
typedef struct
{
int n_rows;
int n_cols;
double **rows;
double *data;
} Matrix;
Matrix *matrix_new (int n_rows, int n_cols)
{
// allocate matrix
Matrix *m = malloc (sizeof(Matrix));
m->n_rows = n_rows;
m->n_cols = n_cols;
// allocate m->data
m->data = malloc (n_rows * n_cols * sizeof(double));
// allocate and fill m->rows
m->rows = malloc (n_rows * sizeof(double*));
for (int i = 0; i < n_rows; i++) {
m->rows[i] = &data[i * n_cols];
}
// set the matrix to 0
for (int i = 0; i < n_rows; i++) {
for (int j = 0; j < n_cols; j++) {
m->rows[i][j] = 0.0;
}
}
return m;
}
The purpose of the rows array it to give you the convenience of being able to refer to element i,j with m->rows[i][j] instead of m->data[i * m->n_cols + j].
To free the matrix, take the inverse steps:
void matrix_free (Matrix *m)
{
free (m->rows);
free (m->data);
free (m);
}
To copy you can simply allocate a matrix of the same size and copy element by element:
Matrix *matrix_copy (Matrix *m1)
{
Matrix *m2 = matrix_new (m1->n_rows, m1->n_cols);
for (int i = 0; i < m1->n_rows; i++) {
for (int j = 0; j < m1->n_cols; j++) {
m2->rows[i][j] = m1->rows[i][j];
}
}
return m2;
}
The important thing to note is that you must not copy the rows array since it is unique to each matrix.
It is important to understand the difference between pointers-to-pointers and multi-dimensional arrays.
What makes it extra confusing is that the same syntax is used for referencing individual elements: var[i][j] will reference element (i,j) of var regardless of if var is a pointer to pointer, double **var or a two-dimensional array, double var[22][43].
What really happens is not the same. A two-dimensional array is a contiguous memory block. A pointer to pointers is an array of pointers that point to the individual rows.
// Two-dimensional array
char var1[X1][X2];
int distance = &var[4][7] - &var[0][0]; // distance = 4*X2+7
// Pointer-to-pointer
char **var2 = malloc(X1 * sizeof(char*)); // Allocate memory for the pointers
for (i=0; i<X1; i++) var2[i] = malloc(X2); // Allocate memory for the actual data
int distance2 = &var2[4][7] - &var[0][0]; // This could result in anything, since the different rows aren't necessarily stored one after another.
The calculation of distance2 invokes undefined behaviour since the C standard doesn't cover pointer arithmetic on pointers that point to different memory blocks.
You want to use pointer-to-pointer. So you need to first allocate memory for an array of pointers and then for each array:
Matrix *make_matrix(int n_rows, int n_cols) {
Matrix *m = xmalloc(sizeof(Matrix));
int i, j;
m->rows = n_rows;
m->cols = n_cols;
m->data = xmalloc(n_rows * sizeof(double*));
for (i=0; i < n_; i++) {
m->data[i] = xmalloc(n_cols * sizeof(double));
for (j=0; j < n_cols; j++) {
m->data[i][j] = 0.0;
}
}
return m;
}
Don't assume that the double 0.0 will have all bits set to 0!
To copy a matrix:
Matrix *copy_matrix(Matrix *source) {
Matrix *m = make_matrix(source->n_rows, source->n_cols);
int i, j;
for (j = 0; j < n_rows; j++) {
for (i = 0; i < n_cols; i++) {
m->data[i][j] = source[i][j];
}
}
return m;
}
I'll backup a bit and start with the basics. Pointers are one of those things that are not difficult to understand technically, but require you to beat your head into the I want to understand pointers wall enough for them to sink in. You understand that a normal variable (for lack of better words) is just a variable that holds a direct-reference to an immediate value in memory.
int a = 5;
Here, a is a label to the memory address that holds the value 5.
A pointer on the other hand, does not directly-reference an immediate value like 5. Instead a pointer holds, as its value, the memory address where 5 is stored. You can also think of the difference this way. A normal variable holds a value, while a pointer holds the address where the value can be found.
For example, to declare a pointer 'b' to the memory address holding 5 above, you would do something like:
int *b = &a;
or equivalently:
int *b = NULL;
b = &a;
Where b is assigned the address of a. To return the value stored at the address held by a pointer, or to operate directly on the value stored at the address held by a pointer, you must dereference the pointer. e.g.
int c = *b; /* c now equals 5 */
*b = 7; /* a - the value at the address pointed to by b, now equals 7 */
Now fast forward to pointer-to-pointer-to-type and simulated 2D matricies. When you declare your pointer-to-pointer-to-type (e.g. int **array = NULL), you are declaring a pointer that points to a pointer-to-type. To be useful in simlated 2D arrays (matricies, etc.), you must delcare an array of the pointer-to-pointer-to-type:
int **array = NULL;
...
array = calloc (NUM, sizeof *array); /* creates 'NUM' pointer-to-pointer-to-int. */
You now have NUM pointers to pointers-to-int that you can allocate memory to hold however many int values and you will assign the starting address for the memory block holding those int values to the pointers you previously allocated. For example, let's say you were to allocate space for an array of 5 random int values (from 1 - 1000) to each of the NUM pointers you allocated above:
for (i = 0; i < NUM; i++) {
array[i] = calloc (5, sizeof **array);
for (j = 0; j < 5; j++)
array[i][j] = rand() % 1000 + 1;
}
You have now assigned each of your NUM pointers (to-pointer-to-int) the starting address in memory where 5 random int values are stored. So your array is now complete. Each of your original NUM pointers-to-pointer-to-int now points to the address for a block of memory holding 5 int values. You can access each value with array notation (e.g. array[i][j] or with pointer notation *(*(array + i) + j) )
How do you free the memory? In the reverse order you allocated (values, then pointers):
for (i = 0; i < NUM; i++)
free (array[i]);
free (array);
Note: calloc both allocates memory and initializes the memory to 0/nul. This is particularly useful for both the pointers and arrays when dealing with numeric values, and when dealing with an unknown number of rows of values to read. Not only does it prevent an inadvertent read from an uninitialized value, but it also allows you to iterate over your array of pointers with i = 0; while (array[i] != NULL) {...}.
So I got this question from a competition website and I am completely puzzled. Here it is:
Which of the following memory allocation code for p[10][10] confirms perfectly to classical definition of an array? The options are:
char **p;
int i;
p = (char**)malloc(10*sizeof(char*));
*p = (char*)malloc(10*sizeof(char));
for(i = i; i<10; i++)
p[i] = p[0] +10*i;
char **p;
int i;
p = (char**)realloc(p, 10*sizeof(char*));
*p = (char*)realloc(p, 10*sizeof(char));
for(i = i; i<10; i++)
p[i] = p[0] +10*i;
char **p = NULL;
int i;
p = (char**)malloc(10*sizeof(char*));
*p = (char*)malloc(10*sizeof(char));
for(i = i; i<10; i++)
p[i] = (char*)realloc(p[i-1], 10*i*sizeof(char));
char **p;
int i;
p = (char**)malloc(10*sizeof(char*));
for(i = i; i<10; i++)
p[i] = (char*)malloc(10*sizeof(char));
I am completely puzzled. Any ideas are most welcome. Thanks.
EDIT: If none of them are are correct, what would be the correct implementation? A bit of explanation is most welcome as I am preparing for aa competitive exam that requres these skills which ask questions like this (most of which has obvious 'side effects' :( ). Also edited the typos.
None. This one does:
char (*p)[10] = malloc(10*sizeof(*p));
Explanation:
p is declared as a pointer to an array of ten chars. Since it points to an array, sizeof(*p) returns the size of that array, which is 10. So, the malloc() call allocates memory for ten such arrays, i. e. 100 bytes.
Now, when you derefence the pointer as in p[3], the value is the fourth array in the slap of memory. However, this value decays to a pointer to its first element in almost all contexts, so when you say p[3][5] the value is the 6th element within the 4th array within the memory slap.
Sorry, if this sounds a bit confusing, but it's really the same thing that's happening when you declare an array with char array[10][10];, except that the memory comes from a different place.
First things first : do not cast the return value of malloc in C. This is not your code, but anyway, it is always good to say.
Also, the loops should be rewritten like so : for(i = 1; i < 10; i++) or maybe for(i = 0; i < 10; i++). i = i is probably an error (what is that website, again ?), and I will assume so.
char **p;
int i;
p = malloc(10*sizeof(char*));
*p = malloc(10*sizeof(char));
for(i = 1; i < 10; i++)
p[i] = p[0] + 10*i;
This first code allocates 10 char* to p, and then 10 char to *p, which is equivalent to p[0]. Then, it puts the addresses of an unallocated 2D array into the other values of p[i] for i from 1 to 9. Since the array is not allocated (or only partially - p[0] is allocated), this is pretty bad.
char **p;
int i;
p = realloc(p, 10*sizeof(char*));
*p = realloc(p, 10*sizeof(char));
for(i = 1; i < 10; i++)
p[i] = p[0] + 10*i;
This second code uses realloc instead of malloc, which does not solve the previous problems, but also adds probable segmentation faults : realloc on an uninitialized pointer is a bad idea.
char **p = NULL;
int i;
p = malloc(10*sizeof(char*));
*p = malloc(10*sizeof(char));
for(i = 1; i < 10; i++)
p[i] = realloc(p[i-1], 10*i*sizeof(char));
This third code does the same as the first one for the first part, but then uses realloc on p[i] for i starting with 0. I assume realloc deallocates the pointer you pass to it, so this is not really a good idea. And the 10*i*sizeof(char) is not a good idea either. Basically, you will have p[9] allocated with 90 char, and nothing else. And, by the way, initializing p to NULL here doesn't do anything.
char **p;
int i;
p = malloc(10*sizeof(char*));
for(i = 0; i < 10; i++)
p[i] = malloc(10*sizeof(char));
This fourth code has something good at least : it allocates separately each p[i] for i from 0 to 9 (I assume i starts with 0 here, unlike in the other codes). So, this might be the answer you are looking for.
Finally, the question is badly formulated since none of these codes have the same semantics as a declaration like char p[10][10].
Edit :
If you want a dynamically allocated 2D array, you can use this for instance :
int i;
char **p = malloc(10 * sizeof *p);
for(i = 0; i < 10; i++)
p[i] = malloc(10 * sizeof *p[i]);
You can also, as cmaster did, use a pointer to an array type (like char[10]). This is the exemple from cmaster : char (*p)[10].
Well let's see:
This will likely lead to errors because p[1][0] (among others) points to invalid memory locations.
This is undefined behavior because you pass an uninitialized pointer to realloc().
That code possibly leaves uninitialized pointers all over the place.
This code loops from [1, 10), where I would expect [0, 10). So it's also terrible.
Therefore:
All of these are terrible. The correct code is either:
char (*p)[10] = malloc(10 * sizeof(*p));
Or:
char **p = malloc(10 * sizeof(*p));
for (int i=0; i<10; ++i)
p[i] = malloc(10 * sizeof(**p));
I have an an integer array of values and I would like to make a double pointer to point to this array. I assume that this 1D integer array actually represents a 2D array. That is for instance that if I have an int A[2000*12] then I have 12 lines and 2000 rows. The problem is that I do not know how to initialize the pointer to point to the array A. My initial thought was like that:
int A[2000*12];
int **ptr;
ptr=&A[0];
Of course this is not correct. More precisely I get the following error:
incompatible pointer types in assignment
Remove the [0] part:
int A[2000*12];
int **ptr;
ptr = &A;
EDIT:
This however does not solve the problem with getting a 2D array. You still can't access it like
A[1][2] = 10;
because the compiler does not know the length of rows.
My favourite way of initializing a 2D array goes like:
int width = 10;
int height = 20;
int *_a = (int *) malloc(width * height * sizeof(int));
int **a = (int **) malloc(height * sizeof(int *));
The first allocation creates the 2D array, the second one creates an array pointing to each row.
int i, offset = 0;
for (i = 0; i < height; i++, offset += width)
{
a[i] = _a + offset;
}
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 2;
a[1][1] = 4;
// ...
I am currently programming in C, and I am creating an array of pointers. These pointers contained in the array will last for the duration of the entire program.
Let's say the array of pointers is array A. I then create another array of pointers B, and I put an element of array A into array B. Then, I free array A.
What will happen to the element in array B? Will it no longer be valid since array A has been freed, or will it still be valid, since the actual pointer is still valid in memory?
Thanks
Here's an example of what my code will look like--
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;
int **array_b = (int **) malloc (sizeof (int *) * 1);
array_b[0] = array_a[0];
free(array_a);
Now, what happens to array_b[0]?
If you do this
int *a = malloc(10 * sizeof(int));
for (int i = 0 ; i != 10 ; i++) {
a[i] = 2*i+1;
}
int *b = a;
free(a);
then b would be invalid as well.
If you do this, however
int *a = malloc(10 * sizeof(int));
for (int i = 0 ; i != 10 ; i++) {
a[i] = 2*i+1;
}
int *b = malloc(10 * sizeof(int));
memcpy(b, a, 10 * sizeof(int));
free(a);
then b remains valid.
The pointers itself doesn't change, it still points where it pointed. The only thing is that the location it points to might be allocated to some other program. You could still write and read the location with undefined behaviour. Check this code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *a = (int *)malloc(3 * sizeof(int));
a[0] = 1, a[1] = 2, a[2] = 3;
free(a);
// a = NULL // if you add this, the following will crash.
printf("%d\n", a[0]);
printf("%d\n", a[1]);
printf("%d\n", a[2]);
return 0;
}
If you are lucky, you could still get the correct result. But it's just luck.
So it's usually good idea to set the pointer to NULL after being freed.
So I just learned a little bit about free() not too long ago, so I'm sorry to say I don't know too much about it yet, but here's the little that I do know:
To return dynamically allocated memory to the system, you use the free() function. My professor used this kind of example:
struct listNode *Bob;
Bob = &any instance of listNode;
free(Bob);
So I believe B will still remain valid while A is no longer referenced. Java periodically collects dynamically allocated memory that is no longer referenced and it goes in the 'garbage.' C doesn't do that, which is why we use free(). Hope that helps a bit. I'm still learning myself. Good question :)
C interprets an array as the address of the base element, so depending on how you have freed the array you may not have freed the element at all.
However, assuming you did free all the elements of the array, your pointer in array B will still be there (it will still point to the same location in memory). However, you really don't know what is at that location because you already freed the memory there. You may still get the original data stored there, or it may have been overwritten. Definitely not safe to use it, though.
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];