0

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.

  • Be aware that there is a big difference between an array of arrays (such as `ar` in `main`) and an array of pointers, such you are creating by your reshaping. I doubt that the difference is something you want. – John Bollinger Jan 30 '18 at 17:00
  • @JohnBollinger I am using array of pointers as I have a very large size of arrays. So I thought pointers would be more efficient to handle. Please correct me if my concept is wrong. –  Jan 30 '18 at 17:09
  • Your concept is wrong. An array of pointers requires more space, is much slower to set up and tear down, and is usually slower to work with than is an array of arrays. You may need dynamic allocation, but you do not need multiple levels of it. – John Bollinger Jan 30 '18 at 17:17
  • @John Bollinger, So while I am making functions and returning them to main. I should send the original array instead of using pointer? Sorry to bother, I am fairly new to C. –  Jan 30 '18 at 17:30
  • Not necessarily. You can dynamically allocate an array of arrays, and thereby return a pointer to one from a function. I will shortly present an answer describing this. – John Bollinger Jan 30 '18 at 18:20

2 Answers2

1

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thank you John for your well explained code and time. I think it will also help others. –  Jan 30 '18 at 20:16
0

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.

user2736738
  • 30,591
  • 5
  • 42
  • 56
  • Thank you. Now code is running fine. I did not notice any undefined behavior with my compiler. So, Adding colsize solving it? one more question in matlab the output is like: `code`1 2 3`code` `code`13 14 15`code` Our first slice output is different. is there a way to achieve that ? –  Jan 30 '18 at 17:07
  • @Asparagus.: I am not aware of matlab's array indexing...but definitely you can do it (There is a logic in there for sure). My answer shows you a way to get to the jagged array (formation of it)..(on the way you showed - correcting the segfault). Given the logic of matlab array that can be done too following a similar logic. – user2736738 Jan 30 '18 at 17:28