简体   繁体   中英

Copy a 2D matrix to a 3D matrix using memcpy in C

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM