I was instructed to copy all elements from my 2d array, matrix2D
, to my 3d array matrix3D
using a single memcpy
command and then print out the 3d array (separating elements with a comma, rows with a semicolon and each 2d matrix with a new line). Before that, the program takes in user input for dimensions of the 3d matrix, and then all the data to go into the matrix (floats). My functions malloc2d
and malloc3d
are working correctly and I am getting no segmentation faults, but when I attempt to memcpy
the data and then print the 3d matrix, nothing is printing. Here is the code:
#include "utilities.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
unsigned int dim1,dim2,dim3;
printf("Dimensionality of 3-D matrix ===>");
scanf("%u",&dim1); //takes user input for dimensions
scanf("%u",&dim2); //takes user input for dimensions
scanf("%u",&dim3); //takes user input for dimensions
//allocating memory for 3d, 2d and 1d matrices
float*** matrix3D = malloc3D((unsigned int)(dim1), (unsigned int)(dim2), (unsigned int)(dim3), sizeof(float));
float** matrix2D = malloc2D(dim1, dim2*dim3, sizeof(float));
float* matrix1D = malloc(dim1*dim2*dim3);
for(int i = 0; i<(dim1*dim2*dim3); i++){ //taking user input floats
fscanf(stdin, "%f", &matrix1D[i]);
}//for
for(int i = 0; i<(dim1*dim2*dim3); i++){//writing contents of 1d matrix to 2d matrix
matrix2D[i/(dim2*dim3)][i%(dim2*dim3)] = matrix1D[i];
}//for
for (int i = 0; i < dim1; i++){//printing out 2d array as asked
for (int j = 0; j < (dim2*dim3); j++){
printf("%0.2f", matrix2D[i][j]);
if(j==((dim2*dim3)-1)){
printf(";");
}//if
else{
printf(",");
}//else
}//for
}//for
printf("\n"); //new line for readability
memcpy(matrix3D, matrix2D, dim1*dim2*dim3*sizeof(float));
for (int i = 0; i < dim1; i++){//printing out 3d array as asked
for (int j = 0; j < dim2; j++){
for(int k = 0; k < dim3; k++){
printf("%0.2f", matrix3D[i][j][k]);
if(j==((dim2)-1)){
printf(";");
}//if
else if(k==((dim3)-1)){
printf("\n");
}//else if
else{
printf(",");
}//else
}//for
}//for
}//for
free3D(matrix3D);
free2D(matrix2D);
free(matrix1D);
} //main
The example input and output given is (I have everything printing except the last 2 lines - the 3d matrix):
Here are malloc2D, malloc3D, free2D and free3D. I was under the impression they were right because they were given to us after none of us were able to get it.
void** malloc2D(size_t rows, size_t cols, size_t sizeOfType){
void* block = malloc(sizeOfType * rows * cols);
void** matrix = malloc(sizeof(void*) * rows);
for (int row = 0; row < rows; ++row) {
matrix[row] = block + cols * row * sizeOfType;
}//for
return matrix;
}//malloc2D
void free2D(void*** matrix){
free((*matrix)[0]);
free((*matrix));
*matrix = NULL;
}//free2D
void*** malloc3D(size_t depth, size_t rows, size_t cols, size_t sizeOfType){
void* block = malloc(sizeOfType * rows * cols * depth);
void** matrix = malloc(sizeof(void*) * rows * depth);
void*** matrix2 = malloc(sizeof(void**) * depth);
for (int depth1 = 0; depth1 < depth; ++depth1) {
matrix2[depth1] = (void**)(matrix + depth * rows * sizeof(void**));
for(int row = 0; row < rows; ++row){
matrix[depth1 * rows + row] = block + (depth1 * rows + row) * cols * sizeOfType;
}//for
}//for
return matrix2;
}//malloc3D
void free3D(void**** matrix){
free((*matrix)[0][0]);
free((*matrix)[0]);
free((*matrix));
*matrix = NULL;
}//free3D
The only way to use a single memcpy
to copy a 2d array into a 3d array is if the data for each of the 2d and 3d arrays are flat in memory.
The allocation functions you were provided show that the data for each are contiguous. Therefore, you need to copy the data section of the 2d array into the data section of the 3d array.
Since the data is contiguous, it follows that the data section is located at the address of the first element of the arrays. Hence, the data section for the 2d array begins at &matrix2D[0][0]
, and the data section for the 3d array begins at &matrix3D[0][0][0]
. Given the semantics of array syntax, these expressions are the same as matrix2D[0]
and matrix3D[0][0]
, respectively.
Assuming that matrix1D
is initialized properly:
memcpy(matrix2D[0], matrix1D, dim1*dim2*dim3*sizeof(float));
memcpy(matrix3D[0][0], matrix2D[0], dim1*dim2*dim3*sizeof(float));
I've seen this allocation code a couple of times, and I always point out that while it's a clever way of allocating space for double/triple pointers, you might encounter problems when not used carefully.
Let's take a look at malloc2D
:
void** malloc2D(size_t rows, size_t cols, size_t sizeOfType){
void* block = malloc(sizeOfType * rows * cols);
void** matrix = malloc(sizeof(void*) * rows);
for (int row = 0; row < rows; ++row) {
matrix[row] = block + cols * row * sizeOfType;
}//for
return matrix;
}
Like I've pointed out before, void*
pointer arithmetic is illegal in C, GNU C has an extension for that. But let's assume this works as intended:
The first malloc
reserves rows * cols
spaces for objects of size sizeOfType
. The second malloc
reserves space for rows
pointers. The memory layout of this is (the arrows represent: pointer points to):
Note that block
and matrix
and different memory locations, the matrix[i]
( i
th row) point to the start of a sequence at block[j]
(start of a new colum, multiple of cols
).
The malloc3D
is basically doing the same but multiple times ( depth
times). The basic memory layout is is similar with one more layer of pointers 1 :
Note that block
, matrix
and matrix2
are different memory locations, the matrix2[i]
point to the start of a sequence at matrix[j]
and the matrix[j]
points to the start of a sequence at block[k]
.
Now when you do:
memcpy(matrix3D, matrix2D, dim1*dim2*dim3*sizeof(float));
you are not copying the contents pointed to by matrix2D
into the contents pointed to by matrix3D
; the contents of matrix2D
are not float
s, they are pointers! So you are in fact copying the pointers of matrix2D
in matrix3D
. That will cause trouble because the pointers are not of the same type (see the graphs): matrix2D
pointers point to float*
and matrix3D
pointers point to float**
, so when you dereference them, you will get wrong results, because you are dereferencing from incorrect addresses. As jxh points out in his/her answer , this would only work when the values are flat in memory, but they are not, even though block
is flatt, but you are not accessing block
directly, you are accessing it through pointers.
That means that
free3D(matrix3D);
free2D(matrix2D);
will get you into trouble, because the first free
does not free the memory reserved by malloc3D
, as it has been overwritten by the memcpy
. free2D
would attempt to free the same memory which you are not allowed to.
You would have to go down to the block
level and use memcpy
there, for example this is OK:
for(int i = 0; i < rows; ++i)
memcpy(matrix3D[0][i], matrix2D[i], cols * sizeof(float));
Fotenotes
1 Because of the limited space in the graph, I use depth == 2
, so that's why matrix2+1
points to matrix+2
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.