-1

I'm trying to refactor my code to make it better/more readable so I'm trying change a 2-D variable array allocation as follows

// OLD CODE
int **map;
        map = calloc(number, sizeof(int *));
        if (!(map)) {
            free(map);
            return 1;
        }
        for (int i = 0; i < number; i++) {
            map[i] = calloc(number, sizeof(int));
            if (!(map[i])) {
                while (--i >= 0) {
                    free(map[i]);
                }
                free(map);
                return 1;
            }
        }

// NEW CODE
int (*map)[number] = malloc(sizeof (int[number][number]));
if (!(map)){
    free(map);
    return 1;
}

The problem is that all my functions that use map take int **map and by changing the declaration of map like i did the IDE tells me incorrect type int[]* instead of int** What should i use instead of int**? Using int[]* map in the function declaration tells me can't resolve variable map

John Doe
  • 1,613
  • 1
  • 17
  • 35
  • 1
    `type func(int n, int (*map)[n]);` call `func(number, map);` – BLUEPIXY Mar 06 '17 at 20:49
  • Do yourself a favour and just create a flat array, then reference rows by array[y*width+x] = value; – Malcolm McLean Mar 06 '17 at 20:53
  • @MalcolmMcLean i was thinking about that, is there a way to allocate half the size of the map and not have to worry about order of indexes? in my map position [i][j] and [j][i] will always have the same value but right now i allocate twice the needed size to not have to worry about the order of i and j. – John Doe Mar 06 '17 at 21:03
  • 1
    To be clear, new code `map` is not a 2D array. It is a "pointer to array number of int". Old `map` is a [pointer to pointer to int](http://cdecl.org/?q=int+**map%3B) – chux - Reinstate Monica Mar 06 '17 at 21:09
  • array[ y*width-y + x] – Malcolm McLean Mar 06 '17 at 21:10
  • Commenting going wrong. The formula is not right. – Malcolm McLean Mar 06 '17 at 21:18
  • @JohnSmith Your current functions take a pointer, a pointer to an array of pointers. `int (*map)[number] = malloc(...);` allocates an array of memory of `int`. There does not exist an equivalent array of pointers to pass to the functions with the new allocation code. Aside from the multiple `malloc()` calls in the "old" code, what is wrong with it? – chux - Reinstate Monica Mar 06 '17 at 21:19
  • 1
    BTW: why the -1 in `calloc((number - 1), sizeof(int *))`? Certainly old code is incorrect with the following `for (int i = 0; i < number; i++) { map[i] = calloc(number, sizeof(int));...` – chux - Reinstate Monica Mar 06 '17 at 21:25
  • @chux as i said, i would like to make the code easier to read, also it would be a plus to have all the cells allocated near each other in memory. as for the minus 1 it was a typo my bad – John Doe Mar 07 '17 at 12:03

2 Answers2

0

Turns out the below code is not a C99 alternative @M.M, but a GCC extension.

Undocumented GCC Extension: VLA in struct


As a C99 GCC extension alternative to int (*map)[number] = malloc(sizeof (int[number][number])); for code simplification and maintain compatibility with existing function set, allocate all the memory needed with 1 *alloc() call.

This does require that when code is done with the map, all the memory is free'd with one free(map). Further, individual rows of map[] can no longer be re-allocated, but can be swapped within the map[].

int **map_allocate(size_t row, size_t column) {
  struct {
    int *ip[row];        // Array of pointers, followed by a ...
    int i[row][column];  // 2D array of int
  } *u;
  u = calloc(1, sizeof *u);
  if (u == NULL) {
    return NULL;
  }
  for (size_t i = 0; i<row; i++) {
    u->ip[i] = u->i[row];
  }
  return &u->ip[0];
}

Note: no casting and field i[][] is properly aligned.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Structs cannot contains VLAs – M.M Mar 07 '17 at 00:28
  • @M.M Interesting. Yet `u` here is is not a `struct`, but a pointer to one - but I'd expect that to not make enough difference. One compiler accepted it (waring well enabled - or so I thought - and hence this post) and another warned, as you say, "a member of a structure or union cannot have a variably modified type". I shall review. – chux - Reinstate Monica Mar 07 '17 at 03:02
  • @M.M Well it looks like I'm back to the drawing board. Too bad , it did look like a nifty solution. – chux - Reinstate Monica Mar 07 '17 at 03:07
0

To use one allocation with standard code, unlike the other answer, is a bit trickier as one needs to insure that a combined memory allocation of pointers and int needs to meet alignment concerns in the unusual case of int alignment requirements exceed pointer alignment ones. This is more easily shown with long long as below.

If this makes "code easier to read" is left to OP's judgment.

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

long long **map_allocate_ll(size_t row, size_t column) {
  long long  **map;
  long long  *ints;

  size_t pointers_sz = sizeof *map * row;
  // extend pointer size to `*ints` boundary
  pointers_sz = (pointers_sz + sizeof *ints - 1)/sizeof *ints * sizeof *ints;
  size_t ints_sz = sizeof *ints * row * column;
  printf("psize %zu, isize %zu\n", pointers_sz, ints_sz);

  map = calloc(1, pointers_sz + ints_sz);
  if (map == NULL) {
    return NULL;
  }
  ints = (void*) ((char*) map + pointers_sz);
  printf("map    %p\n", (void *) map);
  for (size_t i = 0; i<row; i++) {
    map[i] = &ints[i * column];
    printf("map[%zu] %p\n", i, (void *) map[i]);
  }
  return map;
}

int main() {
  free(map_allocate_ll(5,3));
}

Sample output

psize 24, isize 120
map    0x80081868
map[0] 0x80081880
map[1] 0x80081898
map[2] 0x800818b0
map[3] 0x800818c8
map[4] 0x800818e0
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256