( uid_2716 | 2012. 03. 30., p – 22:25 )

I. Ahhoz, hogy a tömb egyben ábrázolható legyen egyáltalán, a méretének kifejezhetőnek kell lennie size_t-ben. Ezért size_t-ben összeszoroznám a dimenziók szerinti méreteket, egyben allokálnám, és az indexeléshez kézzel szoroznék.

Itt további kérdés, hogy a dimenziók darabszáma fix-e, vagy az is dinamikus. Ebben a Usenet post-omban (*) leírok egy módszert, amivel egy K dimenziós tömböt N db közel egyenlő részre lehet szétosztani (N db thread általi párhuzamos bejárásra) -- a most éppen érdekes szorzás a

get_maxflat()

függvényben van.

(*)

Message-ID: <Pine.LNX.4.64.1009062343000.32316@login03.caesar.elte.hu>

II. C99-ben lehet VLA-kat is használni (típusdefinícióra is), csak annak az a baja, hogy a függvény hatásköréből (scope) maga a típus nem tud kijönni. Ilyenkor a (void *)-ot oda-vissza kell cast-olni ugyanerre a (pointer-to-VLA) típusra, ahol csak használni akarjuk, ami elég nyűg, és könnyen el lehet rontani.


/* gcc -o vla -std=c99 -pedantic -Wall -Wextra vla.c */

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

struct ia3d
{
  size_t dim0,
      dim1,
      dim2;
  void *arrptr;
};

static int
ia3d_init(struct ia3d *ia3d, size_t dim0, size_t dim1, size_t dim2)
{
  /* Check for expressibility of full size in bytes. */
  if (0u < dim0 && 0u < dim1 && 0u < dim2
      && dim0               <= (size_t)-1 / dim1
      && dim0 * dim1        <= (size_t)-1 / dim2
      && dim0 * dim1 * dim2 <= (size_t)-1 / sizeof(int)) {
    int (*tmp)[dim0][dim1][dim2];

    tmp = malloc(sizeof *tmp);
    if (0 != tmp) {
      ia3d->dim0 = dim0;
      ia3d->dim1 = dim1;
      ia3d->dim2 = dim2;
      ia3d->arrptr = tmp;

      return 0;
    }
  }

  return -1;
}


static void
ia3d_uninit(struct ia3d *ia3d)
{
  free(ia3d->arrptr);
}


static inline int *
ia3d_ref(struct ia3d *ia3d, size_t ofs0, size_t ofs1, size_t ofs2)
{
  return &(
    *(int (*)[ia3d->dim0][ia3d->dim1][ia3d->dim2])ia3d->arrptr
  )[ofs0][ofs1][ofs2];
}


int
main(void)
{
  int ret;
  struct ia3d my;

  ret = EXIT_FAILURE;
  if (0 == ia3d_init(&my, 3u, 4u, 5u)) {
    size_t ofs0,
        ofs1,
        ofs2;

    /* populate */
    {
      int i;

      i = 0;
      for (ofs0 = 0u; ofs0 < my.dim0; ++ofs0) {
        for (ofs1 = 0u; ofs1 < my.dim1; ++ofs1) {
          for (ofs2 = 0u; ofs2 < my.dim2; ++ofs2) {
            *ia3d_ref(&my, ofs0, ofs1, ofs2) = i++;
          }
        }
      }
    }

    /* use */
    for (ofs0 = 0u; ofs0 < my.dim0; ++ofs0) {
      (void)fprintf(stdout, 0u == ofs0 ? "" : "\n");
      for (ofs1 = 0u; ofs1 < my.dim1; ++ofs1) {
        for (ofs2 = 0u; ofs2 < my.dim2; ++ofs2) {
          (void)fprintf(stdout, "%s%2d", 0u == ofs2 ? "" : " ",
              *ia3d_ref(&my, ofs0, ofs1, ofs2));
        }
        (void)fprintf(stdout, "\n");
      }
    }

    /* done */
    ret = EXIT_SUCCESS;

    /* cleanup */
    ia3d_uninit(&my);
  }

  return ret;
}

Ennyi erővel szerintem jobb szorozni (size_t túlcsordulást szintén ellenőrizve a kezdeti allokációnál, amikor meghatározzuk a teljes elemszámot ill. a teljes méretet byte-ban -- ezt a Usenet post-omban elhanyagoltam). Ráadásul az megy C89-ben is.

III. A kérdésed egyébként egy gyakori C kérdés (C-FAQ 6.16).