If the array is handed off to functions, it decays into a pointer-to-pointer-to-pointer-to-int
, making it unwieldy; one has to pass all the extra size information, too, or pass a pointer to a fixed size; see What is array decaying? A different way of handling arrays with multiple dimensions is an object which has the dimensions encoded within the object. This will compile in C90,
#include <stdlib.h> /* mallc, free, EXIT_ */
#include <errno.h> /* errno */
#include <stdio.h> /* perror, printf, fput[c|s] */
struct IntCube { size_t x, y, z; /* C99 supports FAM; would be useful. */ };
/** Returns a `struct IntCube` with `x`, `y`, `z` dimensions or null and
`errno` may be set. The caller is responsible for calling `free`. */
static struct IntCube *IntCube(const size_t x, const size_t y, const size_t z) {
struct IntCube *cube;
size_t xy_size, xyz_size, data_size, cube_size;
if(!x || !y || !z) return 0;
/* Check for overflow; <https://stackoverflow.com/q/1815367/2472827>. */
xy_size = x * y;
xyz_size = xy_size * z;
data_size = xyz_size * sizeof(int);
cube_size = sizeof cube + data_size;
if(xy_size / x != y
|| xyz_size / xy_size != z
|| data_size / xyz_size != sizeof(int)
|| cube_size < data_size) { errno = ERANGE; return 0; }
/* Allocate memory. */
if(!(cube = malloc(cube_size))) return 0; /* POSIX has defined errors. */
cube->x = x;
cube->y = y;
cube->z = z;
return cube;
}
static int *int_cube_get(const struct IntCube *cube,
const size_t x, const size_t y, const size_t z) {
return (int *)(cube + 1) + z * cube->y * cube->x + y * cube->x + x;
}
typedef void (*IntCubeAction)(const size_t x, const size_t y, const size_t z,
int *pnumber);
typedef void (*BinaryAction)(int bin);
/** Goes through `cube` and performs `action` on each number. It will call
optional binary action `bin` each time there is an
start(false)/end(true)-of-x/y. */
static void IntCubeForEach(struct IntCube *const cube,
const IntCubeAction action, const BinaryAction bin) {
size_t x, y, z;
if(!cube || !action) return;
for(z = 0; z < cube->z; z++) {
if(bin) bin(0);
for(y = 0; y < cube->y; y++) {
if(bin) bin(0);
for(x = 0; x < cube->x; x++) {
action(x, y, z, int_cube_get(cube, x, y, z));
}
if(bin) bin(1);
}
if(bin) bin(1);
}
}
/** @implements IntCubeAction */
static void fill_with_xyz(const size_t x, const size_t y, const size_t z,
int *pnumber) {
*pnumber = (x + 1) * (y + 1) * (z + 1);
}
/** @implements IntCubeAction */
static void print_cube(const size_t x, const size_t y, const size_t z,
int *pnumber) {
(void)y, (void)z;
printf("%s%d", x ? ", " : "", *pnumber);
}
/** @implements BinaryAction */
static void print_cube_corners(int bin) {
printf("%s", bin ? " }" : "{ ");
}
int main(void) {
struct IntCube *cube = 0;
int status = EXIT_FAILURE;
if(!(cube = IntCube(4, 3, 3))) goto catch;
IntCubeForEach(cube, &fill_with_xyz, 0);
IntCubeForEach(cube, &print_cube, &print_cube_corners);
fputc('\n', stdout);
status = EXIT_SUCCESS;
goto finally;
catch:
perror("Cube");
finally:
free(cube);
return status;
}
{ { 1, 2, 3, 4 }{ 2, 4, 6, 8 }{ 3, 6, 9, 12 } }{ { 2, 4, 6, 8 }{ 4, 8, 12, 16 }{ 6, 12, 18, 24 } }{ { 3, 6, 9, 12 }{ 6, 12, 18, 24 }{ 9, 18, 27, 36 } }
This creates a dependence on struct IntCube
, but with the dependence, one can calculate the size at runtime.