2

I'm using gcc version 4.8.0 with flags -Wall -std=gnu99.

I need to dynamically allocate the memory for a contiguous 2D array using malloc in C; this fact is nonnegotiable. However, for ease of use I still want to be able to access the array using the convenient x[r][c] notation. Here's my valiant attempt at creating a pointer to a contiguous 2D array and indexing the array by doing *array[r][c]:

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

int main(void)
{
    size_t rows = 3, cols = 5;

    printf("sizeof(int) = %li\n\n", sizeof(int));

    int (*array)[rows][cols] = malloc(sizeof(int) * rows * cols);
    printf("array starts at %p\n", array);
    printf("sizeof(array) = %li\n", sizeof(array));
    printf("sizeof(array[0][0]) = 0x%lx\n", sizeof(array[0][0]));
    puts("");

    unsigned short r, c;

    for (r = 0; r <= rows - 1; r++) {
        for (c = 0; c <= cols - 1; c++) {
            printf("array[%i][%i] is at %p\n", r, c, &(*array[r][c]));
        };
        puts("");
    };
}

Although it compiles without warnings, it has some unexpected output:

sizeof(int) = 4

array starts at 0x16cc010
sizeof(array) = 8
sizeof(array[0][0]) = 0x14

array[0][0] is at 0x16cc010
array[0][1] is at 0x16cc024
array[0][2] is at 0x16cc038
array[0][3] is at 0x16cc04c
array[0][4] is at 0x16cc060

array[1][0] is at 0x16cc04c
array[1][1] is at 0x16cc060
array[1][2] is at 0x16cc074
array[1][3] is at 0x16cc088
array[1][4] is at 0x16cc09c

array[2][0] is at 0x16cc088
array[2][1] is at 0x16cc09c
array[2][2] is at 0x16cc0b0
array[2][3] is at 0x16cc0c4
array[2][4] is at 0x16cc0d8

I don't really understand what's happening here. The elements are contiguous, but they're way too large for ints, and the array is way too small to hold all of them.

My array declaration is the part I'm most unsure about — I'm reading int (*array)[rows][cols] as "array is a pointer to an array (with stride cols) of ints", but apparently that's incorrect.

What exactly am I doing wrong here?

Dr Kitty
  • 442
  • 3
  • 12
  • Off: why almost everyone is using post-increment instead pre-increment in those places where it is not needed, possibly creating an unnecessary copy of the variable... – Ze.. May 17 '13 at 15:49
  • @Ze_: compilers are smart, there is no difference. (There can be in C++ with overloaded increment operators, but these are primitive types and it isn't even C++.) – huon May 17 '13 at 15:54
  • I understand this, but this isn't Basic-like language which was designed to be as simple and the programmer can be thought of as less... Why write a code which was originally hoping for optimization? – Ze.. May 17 '13 at 15:58
  • @Ze_ this might be true of old and/or dodgy compilers but a modern compiler will optimise that out. – SpacedMonkey May 17 '13 at 16:07
  • Are you saying that (with an older compiler) I should have done `++r` and `++c` instead of `r++` and `c++`? – Dr Kitty May 17 '13 at 16:13
  • No, but post-increment is unnecessary in this case. The value that it returns is not used, so the compiler will most likely replace it with pre-increment. But I see no point in writing initially hoping for compiler wisdom. – Ze.. May 17 '13 at 16:15
  • I didn't initially understand what you meant, but there's a good explanation [here](http://stackoverflow.com/a/9205011/1711424). I think I get it now. – Dr Kitty May 17 '13 at 17:06

2 Answers2

6
int (*array)[cols] = malloc( sizeof *array * rows );

for (r = 0; r <= rows - 1; r++) {
    for (c = 0; c <= cols - 1; c++) {
        printf("array[%i][%i] is at %p\n", r, c, &array[r][c]);

This allows you to index array as array[i][j], since array[i] implicitly dereferences the pointer.

To deallocate, you just need to call

free( array );
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • I tried this before, but apparently I mixed up the row and column sizes in the declaration. This works perfectly and simplifies the syntax. – Dr Kitty May 17 '13 at 16:09
3

The code should be

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

int main(void)
{
    size_t rows = 3, cols = 5;

    printf("sizeof(int) = %li\n\n", sizeof(int));

    int (*array)[rows][cols] = malloc(sizeof(int) * rows * cols);
    printf("array starts at %p\n", array);
    printf("sizeof(array) = %li\n", sizeof(*array));  // Note the *
    printf("sizeof(array[0][0]) = 0x%lx\n", sizeof((*array)[0][0]));
    puts("");

    unsigned short r, c;

    for (r = 0; r <= rows - 1; r++) {
        for (c = 0; c <= cols - 1; c++) {
            printf("array[%i][%i] is at %p\n", r, c, &((*array)[r][c]));
        };
        puts("");
    };
}

since array is a pointer to an array[x][y], you have first to dereference it

 (*array)

to access items

 (*array)[x][y]

This way you get

sizeof(int) = 4

array starts at 0x12de010
sizeof(array) = 60
sizeof(array[0][0]) = 0x4

array[0][0] is at 0x12de010
array[0][1] is at 0x12de014
array[0][2] is at 0x12de018
array[0][3] is at 0x12de01c
array[0][4] is at 0x12de020

array[1][0] is at 0x12de024
array[1][1] is at 0x12de028
array[1][2] is at 0x12de02c
array[1][3] is at 0x12de030
array[1][4] is at 0x12de034

array[2][0] is at 0x12de038
array[2][1] is at 0x12de03c
array[2][2] is at 0x12de040
array[2][3] is at 0x12de044
array[2][4] is at 0x12de048

which is more in the range of values to be expected

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • I suppose I was printing the size of the pointer instead of the size of the array. I feel silly. – Dr Kitty May 17 '13 at 15:55
  • I'm still confused about one thing: since `*array[r][c]` doesn't have the expected precedence, why does it still compile? – Dr Kitty May 17 '13 at 15:56
  • @DrKitty Because there are still 3 levels of indirection. –  May 17 '13 at 16:04
  • I'd accept both answers if I could, since both would work, but I'm going to pick John Bode's because it allows simpler syntax when accessing the array. – Dr Kitty May 17 '13 at 16:11