C pass variable size 2-D array to function

2019-09-19 13:31发布

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

标签: c malloc
2条回答
迷人小祖宗
2楼-- · 2019-09-19 13:51

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
查看更多
3楼-- · 2019-09-19 13:56

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.

查看更多
登录 后发表回答