Reputation: 47
Here I'm trying to allocate the memory dynamically to create multi-dimensional. So I declared a pointer to pointer to pointer :
int ***array ;
And allocated memory for variable array
array = (int***)malloc((sizeof(int) * 2));
This is my code !
void main(void)
{
int matrices, rows, columns;
int ***array;
printf("\n\n HOW MANY MATRICES YOU TO CREATE ? : ");
scanf("%d",&matrices);
array = (int***)malloc((sizeof(int) * matrices));
printf("\n HOW MANY ROWS YOU TO CREATE ? : ");
scanf("%d",&rows);
printf("\n HOW MANY COLUMNS YOU TO CREATE ? : ");
scanf("%d",&columns);
for(int i = 1; i <= matrices; i++)
{
printf("\n Enter %d - matrix! ",i);
for(int j = 1; j <= columns; j++)
{
for(int k = 1; k <= rows; k++)
{
printf("\n Enter element [%d[%d] : ",j,k);
scanf("%d",&array[i][j][k]);
}
}
}
//printing two matrices elements!!!
for(int l = 1; l <= matrices; l++)
{
printf("\n MATRIX - %d !! \n",l);
for(int m = 1; m <= columns; m++)
{
for(int n = 1; n <= rows; n++)
{
printf("%d\t",array[l][m][n]);
}
printf("\n");
}
}
}
But when I try to print the elements of the both matrices, here only second matrix elements are displayed on output for both matrices and very first element in both matrices are displayed with ' 0 ' .
Example :
Input :
First matrix
1 2 3
4 5 6
second matrix
9 8 7
3 5 2
Output :
First matrix
0 8 7
3 5 2
second matrix
0 8 7
3 5 2
I'm new to this site, any mistakes please comment !!
Upvotes: 1
Views: 792
Reputation: 84607
You have not SegFaulted only by happy accident, and due to the fact that the size of a pointer doesn't change. So where you allocate for int*
where you should be allocating for int**
, the size of your allocation isn't affected (by happy accident...)
You generally want to avoid becoming a 3-Star Programmer, but sometimes, as in this case, it is what is required. In allocating for any pointer, or pointer-to-pointer, or in this case a pointer-to-pointer-to-pointer, understand there is no "array" involved whatsoever.
When you declare int ***array;
you declare a single pointer. The pointer then points to (holds the address of) a block of pointers (type int**
) that you allocate. You allocate storage for matricies number of int**
pointers as input by the user.
Each matrix is type int**
, so you must allocate a block of memory containing rows
number of pointer for each matrix.
Finally you allocate cols
number of int
(type int*
) for each and every row in each and every matrix.
So your collection of matricies is an allocated block of pointers with one pointer for each matrix. Then each matrix is an allocate block of pointers with one pointer for every row in that matrix. Finally you allocate a columns worth of int
for each an every row pointer for each and every matrix.
Visually your allocation and assignment would resemble the following:
array (int***)
|
+ allocate matricies number of [Pointers]
|
+----------+
| array[0] | allocate rows number of [Pointers] for each matrix
+----------+ assign to each pointer in array block
| array[1] |
+----------+ array[2] (int**)
| array[2] | <======= +-------------+
+----------+ | array[2][0] |
| .... | +-------------+ allocate cols no. of [int]
| array[2][1] | for each allocated row pointer
+-------------+
| array[2][2] | <=== array[2][2] (int*)
+-------------+ +----------------+
| ... | | array[2][2][0] |
+----------------+
| array[2][2][1] |
+----------------+
| array[2][2][2] |
+----------------+
| ... |
In order to always keep the type-size of each allocation correct, simply use the dereferenced pointer to set the type-size. For example when allocating for array
(int***
) you would use:
array = malloc (matrix * sizeof *array); /* allocate matrix int** */
When allocating for each array[i]
, you would use:
array[i] = malloc (rows * sizeof *array[i]); /* array[i] int** pointers */
Finally when allocating for each block of int
for each row, every array[i][j]
, you would use:
array[i][row] = malloc (cols * sizeof *array[i][row]);
If you always use the dereference pointer to set type-size, you will never get it wrong.
Following the diagram above through and just taking each allocation in turn (and validating EVERY allocation), you could write your allocation and free routines similar to:
/* use dereferenced pointer for type-size */
array = malloc (matrix * sizeof *array); /* allocate matrix int** */
if (!array) { /* validate EVERY allocation */
perror ("malloc-array");
return 1;
}
for (int i = 0; i < matrix; i++) {
array[i] = malloc (rows * sizeof *array[i]); /* array[i] int** pointers */
if (!array[i]) { /* validate */
perror ("malloc-array[i]");
return 1;
}
for (int row = 0; row < rows; row++) {
/* allocate cols int per-row in each matrix */
array[i][row] = malloc (cols * sizeof *array[i][row]);
if (!array[i][row]) {
perror ("malloc-array[i][row]");
return 1;
}
}
}
The complete example that allocates for the number of matricies with the number of rows and columns entered by the user would be:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int ***array = NULL,
matrix,
rows,
cols;
fputs ("no. of matricies: ", stdout);
if (scanf ("%d", &matrix) != 1) /* validate EVERY input */
return 1;
fputs ("no. of rows : ", stdout);
if (scanf ("%d", &rows) != 1) /* ditto */
return 1;
fputs ("no. of cols : ", stdout);
if (scanf ("%d", &cols) != 1) /* ditto */
return 1;
/* use dereferenced pointer for type-size */
array = malloc (matrix * sizeof *array); /* allocate matrix int** */
if (!array) { /* validate EVERY allocation */
perror ("malloc-array");
return 1;
}
for (int i = 0; i < matrix; i++) {
array[i] = malloc (rows * sizeof *array[i]); /* array[i] int** pointers */
if (!array[i]) { /* validate */
perror ("malloc-array[i]");
return 1;
}
for (int row = 0; row < rows; row++) {
/* allocate cols int per-row in each matrix */
array[i][row] = malloc (cols * sizeof *array[i][row]);
if (!array[i][row]) {
perror ("malloc-array[i][row]");
return 1;
}
}
}
/* fill matricies with any values */
for (int i = 0; i < matrix; i++)
for (int j = 0; j < rows; j++)
for (int k = 0; k < cols; k++)
array[i][j][k] = j * cols + k + 1;
/* display each matrix and free all memory */
for (int i = 0; i < matrix; i++) {
printf ("\nmatrix[%2d]:\n\n", i);
for (int j = 0; j < rows; j++) {
for (int k = 0; k < cols; k++)
printf (" %2d", array[i][j][k]);
putchar ('\n');
free (array[i][j]); /* free row of int (int*) */
}
free (array[i]); /* free matrix[i] pointers (int**) */
}
free (array); /* free matricies pointers (int***) */
}
(note: you free the memory for each block of int
before freeing the memory for the block of row pointers in each matrix before freeing the block of pointers to each matrix)
Example Use/Output
$ ./bin/allocate_p2p2p
no. of matricies: 4
no. of rows : 4
no. of cols : 5
matrix[ 0]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 1]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 2]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 3]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/allocate_p2p2p
==9367== Memcheck, a memory error detector
==9367== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9367== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9367== Command: ./bin/allocate_p2p2p
==9367==
no. of matricies: 4
no. of rows : 4
no. of cols : 5
matrix[ 0]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 1]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 2]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
matrix[ 3]:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
==9367==
==9367== HEAP SUMMARY:
==9367== in use at exit: 0 bytes in 0 blocks
==9367== total heap usage: 23 allocs, 23 frees, 2,528 bytes allocated
==9367==
==9367== All heap blocks were freed -- no leaks are possible
==9367==
==9367== For counts of detected and suppressed errors, rerun with: -v
==9367== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
Upvotes: 1
Reputation: 14157
Just use Variable Length Array (VLA) with dynamic storage.
int (*array)[rows + 1][cols + 1] = malloc(sizeof(int[matrices + 1][rows + 1][cols + 1]));
Using VLAs is a lot simpler and more performant.
Adding 1 to each dimension let you address array from index 1 and prevents program from Undefined Behaviour (UB) when accessing element array[matrixes][rows][cols]
.
However, it is BAD practice because arrays in C are indexed from 0. Other approach will confuse other users of your code. Therefore, I strongly encourage you to index arrays from 0 and remove all "+ 1"s.
So the correct allocation code should be:
int (*array)[rows][cols] = malloc(sizeof(int[matrices][rows][cols]));
And update all loops to form:
for(i = 0; i < matrices ; i++)
Finally, free the array when it is no longer used.
free(array)
Upvotes: 2
Reputation: 154178
In C, avoid the model of
pointer = (some_type *) malloc(n * sizeof(some_type)); // Avoid
Instead of allocating to the type, allocate to the referenced object and drop the unneeded cast. Form the size computation with the widest type first. sizeof
operator returns a type of size_t
.
pointer = malloc(sizeof *pointer * n); // Best
This is simpler to code right (OP's (sizeof(int) * matrices)
was incorrect and too small), review and maintain.
Robust code check for allocation errors.
if (pointer == NULL) {
fprintf(stderr, "Out of memory\n"); // Sample error code, adjust to your code's need
exit(-1);
}
Allocate memory for the matrix data, something OP's code did not do.
array = malloc(sizeof *array * matrices);
// Error checking omitting for brevity, should be after each malloc()
// Use zero base indexing
// for(int i = 1; i <= matrices; i++) {
for (int m = 0; m < matrices; m++) {
array[m] = malloc(sizeof *array[m] * rows);
for (int r = 0; r < rows; r++) {
array[m][r] = malloc(sizeof *array[m][r] * columns);
}
}
// Now read in data
// Use data
// Free when done
for (int m = 0; m < matrices; m++) {
for (int r = 0; r < rows; r++) {
free(array[m][r]);
}
free(array[m]);
}
free(array);
Better code would use size_t
for the array dimension's type than int
, yet int
will do for small programs.
Upvotes: 0
Reputation: 163
Since you are using pointer to pointer to pointer. You need to dynamically allocate memory at all stages. At the first stage after you've asked number of matrices. It should be,
array = (int***)malloc (sizeof(int**) * matrices);
Since you are allocating matrices which are int**
. Then after asking number of rows, for each matrix you need to allocate that,
for(i=1 ; i <= matrices ; i++)
array[i-1] = (int**)malloc (sizeof(int*)*ROWS);
Finally you need to allocate memory for each row. So,
for(i=1 ; i <= matrices ; i++)
for(j=1 ; j <= ROWS ; j++)
array[i-1][j-1] = (int*)malloc (sizeof(int)*COLS);
After this you can take inputs at your leisure the way you did. Try this, it should work. If it doesn't, there should be some other problem.
Upvotes: 0