0

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):

example input/output

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
  • How do `malloc2D` and `malloc3D` work? Can you post them? The problem I see, is that `memcpy` copies byte by byte the content from the location pointed to by `matrix2D` into the location pointed to by `matrix3D`. If `matrix3D[i]` and `matrix2d[j]` were initialized with a `malloc`, then you are 1. losing the pointer `matrix3D[i]` by overwriting with `matrix2D[j]`, you would end up with double free issues. – Pablo Feb 20 '18 at 20:45
  • What is `malloc2D` and `malloc3D`? You are probably not allocating the size correctly. Ie: `sizeof(float)` instead of `sizeof(float **)`. Similarly `float* matrix1D = malloc(dim1*dim2*dim3);` is probably wrong. I don't see `sizeof(float)`. – MFisherKDX Feb 20 '18 at 20:46
  • just in general, keep in mind that runs with "no segmentation faults" doesn't mean all is well. – yano Feb 20 '18 at 20:48
  • I put the malloc functions up. Like I said, I was under the impression that they were right because they were given, but I wouldn't be surprised if they weren't. – Ashley Gold Feb 20 '18 at 21:10
  • This setup of contiguous allocation plus a pointer block is just a massive waste of memory, it's not any cheaper to follow a bunch of pointers than it is to do arithmetic to find the location – M.M Feb 21 '18 at 00:07
  • Related to, but wholly separate from, [Modifying function that mallocs a 2D array to a 3D array in C](https://stackoverflow.com/questions/48856272/modifying-function-that-mallocs-a-2d-array-to-a-3d-array-in-c) — with the same OP. – Jonathan Leffler Feb 21 '18 at 01:56

2 Answers2

2

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));
jxh
  • 69,070
  • 8
  • 110
  • 193
0

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] (ith 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 pointers1:

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 floats, 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

1Because of the limited space in the graph, I use depth == 2, so that's why matrix2+1 points to matrix+2.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Pablo
  • 13,271
  • 4
  • 39
  • 59
  • Thank you for taking the time to explain this extensively. I understand what you mean about the void* pointer arithmetic, however I didn't change it because it was given and we are specifically asked not to mess with the given code. This explanation helped me to understand allocation and freeing much better. – Ashley Gold Feb 21 '18 at 04:51