Calloc for an array of array with negative index in C

三世轮回 提交于 2019-12-02 07:50:11

I would allocate a single array:

int* storage = calloc((dim_x + 40) * (dim_y + 40), sizeof(int));

Then I'd return it to the user in an opaque struct like this:

struct map {
  int* storage;
  size_t dim_x;
  size_t dim_y;
};

Then I would define an indexing function for the user:

int* get_cell(struct map* m, x, y) {
  return &m->storage[(x + 20) + (m->dim_x + 40) * (y + 20)];
}

And perhaps to get an entire row if the user wants to read/write multiple elements in one row:

int* get_row(struct map* m, y) {
  return get_cell(m, 0, y);
}

size_t get_row_size(struct map* m) {
  return m->dim_x;
}

Since you want the dimensions to be determined at runtime, you will need to do runtime index calculations in any case.

Jonathan Leffler

Here's my version of the code. The int **map value is what both user and library use to access the data. The Matrix structure encapsulates all the information about the array, including a member that is use to initialize map. The code sets up the sizes, allocates and initializes the structure, then initializes the array, then uses it as a user would, and as the library would. Finally, the allocated memory is freed.

In practice, you'd probably want to split this up into a number of separate functions.

Source: 2da.c:

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "stderr.h"

typedef struct Matrix
{
    int  *map_base;
    int **map_rows;
    int   x_dim;
    int   y_dim;
    int   extra;
    int **map;
} Matrix;

int main(void)
{
    Matrix m;
    int x_dim = 8;
    int y_dim = 6;
    int extra = 5;

    /* Allocation */
    m.extra = extra;
    m.x_dim = x_dim;
    m.y_dim = y_dim;
    int x_cells = x_dim + 2 * extra;
    int y_cells = y_dim + 2 * extra;
    m.map_base = calloc(x_cells * y_cells, sizeof(**m.map));
    if (m.map_base == 0)
        err_syserr("Failed to allocate %zu bytes memory\n",
                   x_cells * y_cells * sizeof(**m.map));
    m.map_rows = calloc(x_cells, sizeof(*m.map));
    if (m.map_rows == 0)
        err_syserr("Failed to allocate %zu bytes memory\n",
                   x_cells * sizeof(*m.map));
    //printf("Map base: 0x%.8" PRIXPTR "\n", (uintptr_t)m.map_base);
    //printf("Map rows: 0x%.8" PRIXPTR "\n", (uintptr_t)m.map_rows);
    for (int i = 0; i < x_cells; i++)
    {
        m.map_rows[i] = &m.map_base[i * y_cells + extra];
        //printf("Row[%2d]   0x%.8" PRIXPTR "\n", i, (uintptr_t)m.map_rows[i]);
    }
    m.map = &m.map_rows[extra];

    int **map = m.map;
    //printf("Map:      0x%.8" PRIXPTR "\n", (uintptr_t)map);

    /* Initialization */
    int x_min = -extra;
    int y_min = -extra;
    int x_max = x_dim + extra;
    int y_max = y_dim + extra;
    printf("Initialization:\n");
    for (int i = x_min; i < x_max; i++)
    {
        for (int j = y_min; j < y_max; j++)
        {
            map[i][j] = i * 100 + j;
            //printf("[%2d,%2d] = %4d\n", i, j, map[i][j]);
        }
    }

    printf("User view:\n");
    for (int i = 0; i < x_dim; i++)
    {
        for (int j = 0; j < y_dim; j++)
            printf("[%2d,%2d] = %4d\n", i, j, map[i][j]);
    }

    printf("Library view:\n");
    for (int i = x_min; i < x_max; i++)
    {
        for (int j = y_min; j < y_max; j++)
            printf("[%2d,%2d] = %4d\n", i, j, map[i][j]);
    }

    /* Deallocation */
    free(m.map_base);
    free(m.map_rows);

    return 0;
}

Example output

When run under valgrind 3.10.0 on Mac OS X 10.9.5 with GCC 4.9.1, I got the following output. The "stderr.h" header is in $HOME/inc and declares err_syserr(), while libjl.a is in $HOME/lib/64 and provides the implementation for err_syserr(), a function that reports the given error message and the error identified by errno and exits. I'm too lazy to write the code in this program this time around. (Search for '[c] user:15168 err_syserr' in SO to find Protect a shared memory segment with semaphore does not work, which has essentially equivalent code. I didn't call err_setarg0() in the code in this answer.)

$ gcc -O3 -g -I/Users/jleffler/inc -std=c11 -Wall -Wextra -Wmissing-prototypes \
>     -Wstrict-prototypes -Wold-style-definition -Werror 2da.c -o 2da \
>     -L/Users/jleffler/lib/64 -ljl
$ valgrind ./2da
==26293== Memcheck, a memory error detector
==26293== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26293== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==26293== Command: 2da
==26293== 
Initialization:
User view:
[ 0, 0] =    0
[ 0, 1] =    1
[ 0, 2] =    2
[ 0, 3] =    3
[ 0, 4] =    4
[ 0, 5] =    5
[ 1, 0] =  100
[ 1, 1] =  101
[ 1, 2] =  102
[ 1, 3] =  103
[ 1, 4] =  104
[ 1, 5] =  105
[ 2, 0] =  200
[ 2, 1] =  201
[ 2, 2] =  202
[ 2, 3] =  203
[ 2, 4] =  204
[ 2, 5] =  205
[ 3, 0] =  300
[ 3, 1] =  301
[ 3, 2] =  302
[ 3, 3] =  303
[ 3, 4] =  304
[ 3, 5] =  305
[ 4, 0] =  400
[ 4, 1] =  401
[ 4, 2] =  402
[ 4, 3] =  403
[ 4, 4] =  404
[ 4, 5] =  405
[ 5, 0] =  500
[ 5, 1] =  501
[ 5, 2] =  502
[ 5, 3] =  503
[ 5, 4] =  504
[ 5, 5] =  505
[ 6, 0] =  600
[ 6, 1] =  601
[ 6, 2] =  602
[ 6, 3] =  603
[ 6, 4] =  604
[ 6, 5] =  605
[ 7, 0] =  700
[ 7, 1] =  701
[ 7, 2] =  702
[ 7, 3] =  703
[ 7, 4] =  704
[ 7, 5] =  705
Library view:
[-5,-5] = -505
[-5,-4] = -504
[-5,-3] = -503
[-5,-2] = -502
[-5,-1] = -501
[-5, 0] = -500
[-5, 1] = -499
[-5, 2] = -498
[-5, 3] = -497
[-5, 4] = -496
[-5, 5] = -495
[-5, 6] = -494
[-5, 7] = -493
[-5, 8] = -492
[-5, 9] = -491
[-5,10] = -490
[-4,-5] = -405
[-4,-4] = -404
[-4,-3] = -403
[-4,-2] = -402
[-4,-1] = -401
[-4, 0] = -400
[-4, 1] = -399
[-4, 2] = -398
[-4, 3] = -397
[-4, 4] = -396
[-4, 5] = -395
[-4, 6] = -394
[-4, 7] = -393
[-4, 8] = -392
[-4, 9] = -391
[-4,10] = -390
[-3,-5] = -305
[-3,-4] = -304
[-3,-3] = -303
[-3,-2] = -302
[-3,-1] = -301
[-3, 0] = -300
[-3, 1] = -299
[-3, 2] = -298
[-3, 3] = -297
[-3, 4] = -296
[-3, 5] = -295
[-3, 6] = -294
[-3, 7] = -293
[-3, 8] = -292
[-3, 9] = -291
[-3,10] = -290
[-2,-5] = -205
[-2,-4] = -204
[-2,-3] = -203
[-2,-2] = -202
[-2,-1] = -201
[-2, 0] = -200
[-2, 1] = -199
[-2, 2] = -198
[-2, 3] = -197
[-2, 4] = -196
[-2, 5] = -195
[-2, 6] = -194
[-2, 7] = -193
[-2, 8] = -192
[-2, 9] = -191
[-2,10] = -190
[-1,-5] = -105
[-1,-4] = -104
[-1,-3] = -103
[-1,-2] = -102
[-1,-1] = -101
[-1, 0] = -100
[-1, 1] =  -99
[-1, 2] =  -98
[-1, 3] =  -97
[-1, 4] =  -96
[-1, 5] =  -95
[-1, 6] =  -94
[-1, 7] =  -93
[-1, 8] =  -92
[-1, 9] =  -91
[-1,10] =  -90
[ 0,-5] =   -5
[ 0,-4] =   -4
[ 0,-3] =   -3
[ 0,-2] =   -2
[ 0,-1] =   -1
[ 0, 0] =    0
[ 0, 1] =    1
[ 0, 2] =    2
[ 0, 3] =    3
[ 0, 4] =    4
[ 0, 5] =    5
[ 0, 6] =    6
[ 0, 7] =    7
[ 0, 8] =    8
[ 0, 9] =    9
[ 0,10] =   10
[ 1,-5] =   95
[ 1,-4] =   96
[ 1,-3] =   97
[ 1,-2] =   98
[ 1,-1] =   99
[ 1, 0] =  100
[ 1, 1] =  101
[ 1, 2] =  102
[ 1, 3] =  103
[ 1, 4] =  104
[ 1, 5] =  105
[ 1, 6] =  106
[ 1, 7] =  107
[ 1, 8] =  108
[ 1, 9] =  109
[ 1,10] =  110
[ 2,-5] =  195
[ 2,-4] =  196
[ 2,-3] =  197
[ 2,-2] =  198
[ 2,-1] =  199
[ 2, 0] =  200
[ 2, 1] =  201
[ 2, 2] =  202
[ 2, 3] =  203
[ 2, 4] =  204
[ 2, 5] =  205
[ 2, 6] =  206
[ 2, 7] =  207
[ 2, 8] =  208
[ 2, 9] =  209
[ 2,10] =  210
[ 3,-5] =  295
[ 3,-4] =  296
[ 3,-3] =  297
[ 3,-2] =  298
[ 3,-1] =  299
[ 3, 0] =  300
[ 3, 1] =  301
[ 3, 2] =  302
[ 3, 3] =  303
[ 3, 4] =  304
[ 3, 5] =  305
[ 3, 6] =  306
[ 3, 7] =  307
[ 3, 8] =  308
[ 3, 9] =  309
[ 3,10] =  310
[ 4,-5] =  395
[ 4,-4] =  396
[ 4,-3] =  397
[ 4,-2] =  398
[ 4,-1] =  399
[ 4, 0] =  400
[ 4, 1] =  401
[ 4, 2] =  402
[ 4, 3] =  403
[ 4, 4] =  404
[ 4, 5] =  405
[ 4, 6] =  406
[ 4, 7] =  407
[ 4, 8] =  408
[ 4, 9] =  409
[ 4,10] =  410
[ 5,-5] =  495
[ 5,-4] =  496
[ 5,-3] =  497
[ 5,-2] =  498
[ 5,-1] =  499
[ 5, 0] =  500
[ 5, 1] =  501
[ 5, 2] =  502
[ 5, 3] =  503
[ 5, 4] =  504
[ 5, 5] =  505
[ 5, 6] =  506
[ 5, 7] =  507
[ 5, 8] =  508
[ 5, 9] =  509
[ 5,10] =  510
[ 6,-5] =  595
[ 6,-4] =  596
[ 6,-3] =  597
[ 6,-2] =  598
[ 6,-1] =  599
[ 6, 0] =  600
[ 6, 1] =  601
[ 6, 2] =  602
[ 6, 3] =  603
[ 6, 4] =  604
[ 6, 5] =  605
[ 6, 6] =  606
[ 6, 7] =  607
[ 6, 8] =  608
[ 6, 9] =  609
[ 6,10] =  610
[ 7,-5] =  695
[ 7,-4] =  696
[ 7,-3] =  697
[ 7,-2] =  698
[ 7,-1] =  699
[ 7, 0] =  700
[ 7, 1] =  701
[ 7, 2] =  702
[ 7, 3] =  703
[ 7, 4] =  704
[ 7, 5] =  705
[ 7, 6] =  706
[ 7, 7] =  707
[ 7, 8] =  708
[ 7, 9] =  709
[ 7,10] =  710
[ 8,-5] =  795
[ 8,-4] =  796
[ 8,-3] =  797
[ 8,-2] =  798
[ 8,-1] =  799
[ 8, 0] =  800
[ 8, 1] =  801
[ 8, 2] =  802
[ 8, 3] =  803
[ 8, 4] =  804
[ 8, 5] =  805
[ 8, 6] =  806
[ 8, 7] =  807
[ 8, 8] =  808
[ 8, 9] =  809
[ 8,10] =  810
[ 9,-5] =  895
[ 9,-4] =  896
[ 9,-3] =  897
[ 9,-2] =  898
[ 9,-1] =  899
[ 9, 0] =  900
[ 9, 1] =  901
[ 9, 2] =  902
[ 9, 3] =  903
[ 9, 4] =  904
[ 9, 5] =  905
[ 9, 6] =  906
[ 9, 7] =  907
[ 9, 8] =  908
[ 9, 9] =  909
[ 9,10] =  910
[10,-5] =  995
[10,-4] =  996
[10,-3] =  997
[10,-2] =  998
[10,-1] =  999
[10, 0] = 1000
[10, 1] = 1001
[10, 2] = 1002
[10, 3] = 1003
[10, 4] = 1004
[10, 5] = 1005
[10, 6] = 1006
[10, 7] = 1007
[10, 8] = 1008
[10, 9] = 1009
[10,10] = 1010
[11,-5] = 1095
[11,-4] = 1096
[11,-3] = 1097
[11,-2] = 1098
[11,-1] = 1099
[11, 0] = 1100
[11, 1] = 1101
[11, 2] = 1102
[11, 3] = 1103
[11, 4] = 1104
[11, 5] = 1105
[11, 6] = 1106
[11, 7] = 1107
[11, 8] = 1108
[11, 9] = 1109
[11,10] = 1110
[12,-5] = 1195
[12,-4] = 1196
[12,-3] = 1197
[12,-2] = 1198
[12,-1] = 1199
[12, 0] = 1200
[12, 1] = 1201
[12, 2] = 1202
[12, 3] = 1203
[12, 4] = 1204
[12, 5] = 1205
[12, 6] = 1206
[12, 7] = 1207
[12, 8] = 1208
[12, 9] = 1209
[12,10] = 1210
==26293== 
==26293== HEAP SUMMARY:
==26293==     in use at exit: 29,341 bytes in 374 blocks
==26293==   total heap usage: 452 allocs, 78 frees, 36,581 bytes allocated
==26293== 
==26293== LEAK SUMMARY:
==26293==    definitely lost: 0 bytes in 0 blocks
==26293==    indirectly lost: 0 bytes in 0 blocks
==26293==      possibly lost: 0 bytes in 0 blocks
==26293==    still reachable: 4,096 bytes in 1 blocks
==26293==         suppressed: 25,245 bytes in 373 blocks
==26293== Rerun with --leak-check=full to see details of leaked memory
==26293== 
==26293== For counts of detected and suppressed errors, rerun with: -v
==26293== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$

The 'still reachable' block is the buffer for stdout; if I included fclose(stdout); before returning from main(), it would go. The Mac OS X runtime library allocates a lot of memory too, but that usage is identified in the suppressed information.


Comment and response

I do the whole initiallization as you see in 2 lines. But it is not the initialization that's the problem; the problem is that in every frame the values of map are changed. So I need just a 'map' which points directly to 'boundaries map'. map[0][0] is boundaries_map[20][20] and so on. I tried to declare global **map and then write my second line into the main but I get an error about cast. (*map)[dim_x+40] this is a **map isn't it?

I'm not sure I completely understand what you're after, which is one reason this response has taken a long time.

You cannot have global variables that are VLAs; they can only be allocated in functions, either on the stack as local variables or on the heap via malloc() et al. The original code I showed allows you to define an array via a global variable, a double pointer, that can be accessed from any code that can read the double pointer.

The code below could be considered an exercise in constructing VLAs. It uses malloc() to create a VLA, recording where it was created in a pointer to a VLA. This can be passed to a function just like a stack-based VLA can be passed. The code compiles cleanly under GCC 4.9.1 on Mac OS X 10.9.5:

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Werror \
>     2dv.c -o 2dv 

It runs cleanly and leak-free according to valgrind.

Source: 2dv.c:

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

static void dump_vla_1(int x_size, int y_size, int vla[x_size][y_size]);
static void dump_vla_2(int x_min, int y_min, int x_size, int y_size,
                       int vla[x_size][y_size]);
static void dump_vla_3(int x_min, int x_max, int y_min, int y_max,
                       int x_size, int y_size, int vla[x_size][y_size]);

int main(void)
{
    int extra = 3;
    int x_base = 5;
    int y_base = 4;
    int x_size = x_base + 2 * extra;
    int y_size = y_base + 2 * extra;

    int (*vla)[x_size][y_size] = calloc(x_size * y_size, sizeof(int));

    for (int i = 0; i < x_size; i++)
        for (int j = 0; j < y_size; j++)
            (*vla)[i][j] = (i + 1) * 100 + (j + 1);

    for (int i = 0; i < x_size; i++)
    {
        for (int j = 0; j < y_size; j++)
            printf(" %4d", (*vla)[i][j]);
        putchar('\n');
    }

    dump_vla_1(x_size, y_size, *vla);
    dump_vla_2(0, 0, x_size, y_size, *vla);

    /* Harsh cast! */
    int (*vla_2)[x_size][y_size] = (int (*)[x_size][y_size])&(*vla)[extra][extra];

    dump_vla_2(-extra, -extra, x_size, y_size, *vla_2);

    dump_vla_3(-extra, x_size - extra, -extra, y_size - extra, x_size, y_size, *vla_2);
    dump_vla_3(0, x_base, 0, y_base, x_size, y_size, *vla_2);

    free(vla);
    return 0;
}

static void dump_vla_1(int x_size, int y_size, int vla[x_size][y_size])
{
    printf("Matrix %dx%d\n", x_size, y_size);
    for (int i = 0; i < x_size; i++)
    {
        for (int j = 0; j < y_size; j++)
            printf(" %4d", vla[i][j]);
        putchar('\n');
    }
}

static void dump_vla_2(int x_min, int y_min, int x_size, int y_size,
                       int vla[x_size][y_size])
{
    printf("Matrix %dx%d (%d..%d, %d..%d)\n",
           x_size, y_size, x_min, x_min + x_size - 1, y_min, y_min + y_size - 1);
    for (int i = x_min; i < x_min + x_size; i++)
    {
        for (int j = y_min; j < y_min + y_size; j++)
            printf(" %4d", vla[i][j]);
        putchar('\n');
    }
}

static void dump_vla_3(int x_min, int x_max, int y_min, int y_max,
                       int x_size, int y_size, int vla[x_size][y_size])
{
    printf("Matrix %dx%d (%d..%d, %d..%d)\n",
           x_size, y_size, x_min, x_max, y_min, y_max);
    for (int i = x_min; i < x_max; i++)
    {
        for (int j = y_min; j < y_max; j++)
            printf(" %4d", vla[i][j]);
        putchar('\n');
    }
}

Note the use of *vla and *vla_2 to pass the VLA to the functions. Since the vla and vla_2 variables are pointers to a VLA, *vla is the VLA which the functions expect.

Output:

  101  102  103  104  105  106  107  108  109  110
  201  202  203  204  205  206  207  208  209  210
  301  302  303  304  305  306  307  308  309  310
  401  402  403  404  405  406  407  408  409  410
  501  502  503  504  505  506  507  508  509  510
  601  602  603  604  605  606  607  608  609  610
  701  702  703  704  705  706  707  708  709  710
  801  802  803  804  805  806  807  808  809  810
  901  902  903  904  905  906  907  908  909  910
 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
Matrix 11x10
  101  102  103  104  105  106  107  108  109  110
  201  202  203  204  205  206  207  208  209  210
  301  302  303  304  305  306  307  308  309  310
  401  402  403  404  405  406  407  408  409  410
  501  502  503  504  505  506  507  508  509  510
  601  602  603  604  605  606  607  608  609  610
  701  702  703  704  705  706  707  708  709  710
  801  802  803  804  805  806  807  808  809  810
  901  902  903  904  905  906  907  908  909  910
 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
Matrix 11x10 (0..10, 0..9)
  101  102  103  104  105  106  107  108  109  110
  201  202  203  204  205  206  207  208  209  210
  301  302  303  304  305  306  307  308  309  310
  401  402  403  404  405  406  407  408  409  410
  501  502  503  504  505  506  507  508  509  510
  601  602  603  604  605  606  607  608  609  610
  701  702  703  704  705  706  707  708  709  710
  801  802  803  804  805  806  807  808  809  810
  901  902  903  904  905  906  907  908  909  910
 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
Matrix 11x10 (-3..7, -3..6)
  101  102  103  104  105  106  107  108  109  110
  201  202  203  204  205  206  207  208  209  210
  301  302  303  304  305  306  307  308  309  310
  401  402  403  404  405  406  407  408  409  410
  501  502  503  504  505  506  507  508  509  510
  601  602  603  604  605  606  607  608  609  610
  701  702  703  704  705  706  707  708  709  710
  801  802  803  804  805  806  807  808  809  810
  901  902  903  904  905  906  907  908  909  910
 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
Matrix 11x10 (-3..8, -3..7)
  101  102  103  104  105  106  107  108  109  110
  201  202  203  204  205  206  207  208  209  210
  301  302  303  304  305  306  307  308  309  310
  401  402  403  404  405  406  407  408  409  410
  501  502  503  504  505  506  507  508  509  510
  601  602  603  604  605  606  607  608  609  610
  701  702  703  704  705  706  707  708  709  710
  801  802  803  804  805  806  807  808  809  810
  901  902  903  904  905  906  907  908  909  910
 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
Matrix 11x10 (0..5, 0..4)
  404  405  406  407
  504  505  506  507
  604  605  606  607
  704  705  706  707
  804  805  806  807
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!