user8126672
user8126672

Reputation:

reshape 2D array into 3D array C

Hi I am trying to convert matlab code in C . While doing that I have to reshape a 2d array into 3d array. I tried to write a function which is given below. I took help from here.

#include <stdio.h>
#include <stdlib.h>

#define ZALLOC(item, n, type) if ((item = (type *)calloc((n), sizeof(type))) == NULL) \
                                  fatalx("Unable to allocate %d unit(s) for item\n", n)
int i,j,k,x,y;
static void fatalx(const char *str, size_t n)
{
    fprintf(stderr, "%s: %zu\n", str, n);
    exit(1);
}

static int ***alloc_3d(int ar[][12],int rows, int cols,int levels)
{

    int count = 0;
    int ***array_3d;
    ZALLOC(array_3d, levels, int **);
    for (i = 0; i < levels; i++)
    {
        int **data;
        ZALLOC(data, rows, int *);
        array_3d[i] = data;
        for (j = 0; j < rows; j++)
        {
            int *entries;
            ZALLOC(entries, cols, int);
            array_3d[i][j] = entries;
            for (k = 0; k < cols; k++)
            {
                array_3d[i][j][k] = ar[i][j];
            }
        }
    }
    return array_3d;
}

static void print_3d(int ***a3d, int rows, int cols,int levels)
{
    for (i = 0; i < levels; i++)
    {
        printf("%d:\n", i);
        for (j = 0; j < rows; j++)
        {
            printf("   %d:  ", j);
            for (k = 0; k < cols; k++)
                printf(" %3d", a3d[i][j][k]);
            putchar('\n');
        }
    }
}

static void free_3d(int ***a3d, int levels, int rows)
{
    for (i = 0; i < levels; i++)
    {
        for (j = 0; j < rows; j++)
            free(a3d[i][j]);
        free(a3d[i]);
    }
    free(a3d);
}

int main(void)
{
    int ar[2][12]={
         {1,2,3,4,5,6,7,8,9,10,11,12},
         {13,14,15,16,17,18,19,20,21,22,23,24}
         };
    int d1 = 2;
    int d2 = 3;
    int d3 = 4;
    int ***a3d = alloc_3d(ar,d1, d2, d3);

    print_3d(a3d, d1, d2, d3);
    free_3d(a3d, d3, d2);

    return(0);
}

This not only giving me wrong values but also garbage values. Where matlab output for first slice is:

a3d(:,:,1) =

 1     2     3
13    14    15

mine one is totally different with

0:
   0:     1   1   1
   1:     2   2   2
1:
   0:    13  13  13
   1:    14  14  14
2:
   0:   1991011277 1991011277 1991011277
   1:     4   4   4
3:
   0:     1   1   1
   1:   6630248 6630248 6630248

As you can see there is garbage value too. So my indexing is also wrong. Any idea how to properly do that? Thanks in advance.

Upvotes: 0

Views: 983

Answers (2)

John Bollinger
John Bollinger

Reputation: 180201

Your example does not, in fact, perform a reshaping, inasmuch as it creates a composite object of a completely different type from that of the original array.

A C multidimensional array is an array of arrays (of arrays ...). Among other significant characteristics, all of the elements of such an array are contiguous in memory. You can also construct an array of pointers, and initialize each pointer to point to an array of its own, etc.. Although these kinds of objects are superficially similar in that you can apply the indexing operator to to both in about the same way, it is important to understand that:

  • The array of pointers requires additional space for the pointers, above and beyond the space to which they point.
  • Although the pointers in an array of pointers are contiguous in memory, the arrays they point to very well might not be. That often leads to
    • wasted space if the system's page size does not evenly divide the pointed-to arrays' sizes, and
    • poorer performance for accessing array elements as a result of poorer locality of reference.
  • The array of pointers is messier and slower to allocate, because multiple separate calls are required to memory allocation functions.
  • The array of pointers is messier and slower to free, because all the pointed-to arrays must also be freed, separately.
  • On the other hand, the array of pointers accommodates "ragged" multi-dimensional pseudo-arrays, were the (pointed to) member arrays are not all the same length.

Here's how your program might look if written using standard C multidimensional arrays (== arrays of arrays):

#include <stdlib.h>
#include <stdio.h>

#define ALLOC(p, n) do {                                \
    if (!((p) = calloc((n), sizeof(*(p))))) {           \
        fprintf(stderr, "Memory allocation failure\n"); \
        exit(1);                                        \
    }                                                   \
} while (0)

void *reshape_2d_3d(size_t id1, size_t id2, int iar[][id2],
        size_t od1, size_t od2, size_t od3) {
    // oar is a pointer to a multidimensional array; in this case, it will
    // point to the first element of an array of arrays (of arrays).
    int (*oar)[od2][od3];
    size_t size1 = id1 * id2;
    size_t size2 = od1 * od2 * od3;
    size_t min_size = (size1 <= size2) ? size1 : size2;

    ALLOC(oar, od1);

    // A loop nest could be used here, too, but I find this simpler for
    // tracking the correspondence of array elements.  It also better
    // accommodates the case where the reshaped result has different overall
    // size from the original.
    for (size_t i = 0; i < min_size; i++) {
        oar[i / (od2 * od3)][(i / od3) % od2][i % od3] = iar[i / id2][i % id2];
    }

    return oar;
}

void print_3d(size_t levels, size_t rows, size_t cols, int ar[][rows][cols]) {
    for (int i = 0; i < levels; i++) {
        printf("%d:\n", i);
        for (int j = 0; j < rows; j++) {
            printf("   %d:  ", j);
            for (int k = 0; k < cols; k++) {
                printf(" %3d", ar[i][j][k]);
            }
            putchar('\n');
        }
    }
}

int main(void) {
    int ar[2][12] = {
            {1,2,3,4,5,6,7,8,9,10,11,12},
            {13,14,15,16,17,18,19,20,21,22,23,24}
        };
    int d1 = 2, d2 = 3, d3 = 4;
    int (*a3d)[d2][d3] = reshape_2d_3d(2, 12, ar, d1, d2, d3);

    print_3d(d1, d2, d3, a3d);

    // A single, simple free() is all that's needed
    free(a3d);
}

Output:

0:
   0:     1   2   3   4
   1:     5   6   7   8
   2:     9  10  11  12
1:
   0:    13  14  15  16
   1:    17  18  19  20
   2:    21  22  23  24

Note that that uses variable-length arrays, but without the usual concern about stack allocation. It therefore requires a conforming C99 compiler, or a conforming C2011 compiler that implements the VLA optional (in that version) feature.

Upvotes: 1

user2736738
user2736738

Reputation: 30926

Your way of assigning the numbers to the newly allocated memory is wrong.

static int ***alloc_3d(int ar[][12],int rows, int cols,int levels,int colsize)
{

    int count = 0;
    int ***array_3d;
    ZALLOC(array_3d, levels, int **);
    int i1=0,j1=0;
    for (i = 0; i < levels; i++)
    {       ...
            ...
            for (k = 0; k < cols; k++)
            {
                array_3d[i][j][k] = ar[i1][j1++];
                if( j1 == colsize) i1++,j1=0;
            }
        }
    }
    return array_3d;
}

Call like this

int colsize = 12;
int ***a3d = alloc_3d(ar,d1, d2, d3,colsize);

This prints:

0:
   0:     1   2   3
   1:     4   5   6
1:
   0:     7   8   9
   1:    10  11  12
2:
   0:    13  14  15
   1:    16  17  18
3:
   0:    19  20  21
   1:    22  23  24

A small note - earlier your code had undefined behavior accessing array index out of the bound.

Upvotes: 0

Related Questions