3

I have a function which tries to loop through a 2D array in a struct:

typedef struct node
{
    int grid[3][3];
} Node;


void someFunction(Node *node) {
     int grid[3][3] = node->grid;
     //loop through
}

When I try to compile this however I get a

mp.c:42: error: invalid initializer

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jeune
  • 3,498
  • 5
  • 43
  • 54
  • One thing, why not just pass an int[][3] instead of wrapping it in a node since it's only a 1 member struct? – Jesus Ramos Jul 21 '11 at 01:58
  • @Jesus Ramos I plan to put other attributes in the struct. – Jeune Jul 21 '11 at 02:06
  • 1
    @Jesus Ramos: Even though Jeune says it won't be like that, it's a common way to make an array a "first class" type. Simply for bringing advantages like allowing assignments (which automatically duplicates the array) or returning arrays from functions. – sidyll Jul 21 '11 at 02:08
  • Ok just making sure you're not complicating things for yourself too much :) – Jesus Ramos Jul 21 '11 at 02:12
  • You should try to understand the difference between an array and a pointer. – Stan Jul 21 '11 at 02:25

3 Answers3

11

You cannot assign arrays in C. It is simply not allowed. When you wrote:

int grid[3][3] = node->grid;

you were attempting to initialize the local array, grid, from the passed in node. If that were permitted (which it isn't), then you'd not need a loop afterwards.

You can assign structures, though, even if they contain arrays, so if the local structure were a Node, you could have written:

Node local = *node;

You would not need to loop through the arrays afterwards to initialize local.

You can loop through the arrays, doing the copy one element at a time:

for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
        grid[i][j] = node->grid[i][j];

You can also use memmove() or memcpy():

int grid[3][3];

assert(sizeof(grid) == sizeof(node->grid));
memcpy(grid, node->grid, sizeof(grid));

At one time, another answer suggested:

Change the line:

int grid[3][3] = node->grid;

to:

int **grid = node->grid;

I noted that this will not work - and was legitimately challenged to explain why. That requires space and formatting.

First of all, the compiler notes:

warning: initialization from incompatible pointer type

That is saying 'you are playing with fire'.

Let's suppose we ignore that warning. The local grid now points at the top-left corner of the array (if you see arrays growing down and across from left-to-right). The value stored there is a plain number, not an initialized pointer, but when the compiler evaluates grid[0], it is forced to assume that will produce a pointer. If node->grid[0][0] contains a zero, you will probably get a segmentation fault and core dump for dereferencing a null pointer (on the assumption that pointers and int are the same size, which is generally true on 32-bit systems), or some other undefined behaviour. If node->grid[0][0] contains another value, then the behaviour is still undefined, but not quite as predictable.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • and then I can loop through grid like a normal 2d array? – Jeune Jul 21 '11 at 01:59
  • @Jeune: yes, it is simply an initialized array with a copy of the contents of the other, which you can manipulate in any that you find necessary. That works regardless of which (compilable) mechanism you use to initialize the local array. – Jonathan Leffler Jul 21 '11 at 02:02
  • Just note that you will be making a copy of the array here, instead of just using the array that is in the structure already. That may be what you want, but if you just want a local variable that points to the same array that lives in the struct, see my answer below. – Jonathan DeCarlo Jul 21 '11 at 02:05
  • I get this though: incompatible implicit declaration of built-in function ‘memcpy’ Do I have to import anything? – Jeune Jul 21 '11 at 02:09
  • @Jeune: `#include ` provides the declaration of `memcpy()`. – Jonathan Leffler Jul 21 '11 at 02:10
  • Interesting formatting on your 3rd code snippet. Very Python-ish :P – sidyll Jul 21 '11 at 02:11
  • 1
    You *can*, however, use `int (*grid)[3] = node->grid`, and this may actually be the most appropriate in this case. – caf Jul 21 '11 at 07:45
1

If you don't want to do the copy, just want a pointer to the array in the struct (note: if you assign values to *pointer, the content of the array in the struct will be changed), you can achieve this aim in two ways:

#include <stdio.h>

typedef struct node
{
    int grid[3][3];
} Node;


void someFunction1(Node *node) {
     int i, j;
     int (*grid)[3] = node->grid;
     for(i=0; i<3; i++){
         for(j=0; j<3; j++){
             printf("%d ", grid[i][j]);
         }
         printf("\n");
     }
}

void someFunction2(Node *node) {
     int i, j;
     int *grid = (int*) node->grid;
     for(i=0; i<3; i++){
         for(j=0; j<3; j++){
             printf("%d ", grid[i*3+j]); // i * column_number + j
         }
         printf("\n");
     }
}

int main()
{
    Node t;
    int i, *p;

    //initialization: t.grid[0][0]=0,  ..., t.grid[2][2]=8
    for(i=0, p=(int*)t.grid; i<9; p++, i++){
        *p = i;
    }

    printf("Function1:\n");
    someFunction1(&t);

    printf("Function2:\n");
    someFunction2(&t);

    return 0;
}

The above code shows a simple function to use pointers. They are all safe and up to standard.

If you want to use pointer to pointer, int**, you will have to make it in a different way, because, arrays are linear storage in memory (so the above code can use an int* to point to the beginning of the array and operate it), but int** not.

EDIT

So here comes the someFunction3()

void someFunction3(Node *node)
{
    int i, j;
    int **p;

    // 3 is the row number. Ignore checking malloc failure
    p = malloc(sizeof(int)*3);
    for(i=0; i<3; i++) {
        p[i] = (int*) node->grid[i]; //assign address of each row of array to *p
    }

    for(i=0; i<3; i++) {
        for(j=0; j<3; j++) {
            printf("%d ", p[i][j]);
        }
        printf("\n");
    }

    free(p);
}
Stan
  • 787
  • 5
  • 16
  • You can avoid the casts when initialising the `int *` values by using `&node->grid[0][0]` (or just `node->grid[0]`). – caf Jul 21 '11 at 07:44
  • @caf: Thanks. You're right. It's my bad habit. I'm lazy to consider the type issue :-p – Stan Jul 21 '11 at 07:55
0

See this other thread for the same question and the answer that doesn't involve copying the memory:

Create a pointer to two-dimensional array

Community
  • 1
  • 1
Jonathan DeCarlo
  • 2,798
  • 1
  • 20
  • 24
  • 1
    No; that won't work. To work, each of `grid[0]`, `grid[1]` and `grid[2]` would have to be a pointer to array, and despite appearances, it is not. The address in `node->grid` is assigned to `grid`, but `grid[1]` is not properly initialized. It is hard to explain this in an answer; it is entirely non-trivial in a comment. – Jonathan Leffler Jul 21 '11 at 02:05
  • I get this warning though: mp.c:42: warning: initialization from incompatible pointer type what does that mean? – Jeune Jul 21 '11 at 02:07
  • Check out this thread for more info: http://stackoverflow.com/questions/1052818/c-create-a-pointer-to-two-dimensional-array – Jonathan DeCarlo Jul 21 '11 at 02:12
  • I'm going to change my answer to this other duplicate thread. @Jonathan: Thanks for the info. – Jonathan DeCarlo Jul 21 '11 at 02:14