This is not only a very ugly peice of software code, but also a very bad one (and I'm not talking about the missing error checks, let's just ignore them):
int ***mdarr = (int ***)malloc(n1 * sizeof(**int)); for (size_t i = 0; i < n1; i++) { mdarr[i] = (int **)malloc(n2 * sizeof(*int)); for (size_t j = 0; j < n2; j++) { mdarr[i][j] = (int *)malloc(n3 * sizeof(int)); for (size_t k = 0; k < n3; k++) mdarr[i][j][k] = get_data(i, j, k); } }
Obviously this was written by a C++-programmer. Let's clean it up a bit: we don't need to cast void-pointers in C.
int ***mdarr = malloc(n1 * sizeof(**int)); for (size_t i = 0; i < n1; i++) { mdarr[i] = malloc(n2 * sizeof(*int)); for (size_t j = 0; j < n2; j++) { mdarr[i][j] = malloc(n3 * sizeof(int)); for (size_t k = 0; k < n3; k++) mdarr[i][j][k] = get_data(i, j, k); } }
Much better, but still, we want one allocation of contiguous virtually memory.
int *mdarr = malloc(n1 * n2 * n3 * sizeof(int)); for (size_t i = 0; i < n1; i++) for (size_t j = 0; j < n2; j++) for (size_t k = 0; k < n3; k++) mdarr[i*n2*n3 + j*n3 + k] = get_data(i, j, k);
Very nice. But we can do better still. Using macros, we can removing the addressing logic, but we have C99, so we will not go that route.
int (*mdarr)[n1][n2][n3] = malloc(sizeof(*mdarr)); for (size_t i = 0; i < n1; i++) for (size_t j = 0; j < n2; j++) for (size_t k = 0; k < n3; k++) (*mdarr)[i][j][k] = get_data(i, j, k);
Perfect, but if you know that it fits on the stack (you probably don't know that), you can (depending on the context) use C99's variable array-size feature:
int mdarr[n1][n2][n3]; for (size_t i = 0; i < n1; i++) for (size_t j = 0; j < n2; j++) for (size_t k = 0; k < n3; k++) mdarr[i][j][k] = get_data(i, j, k);