在结构体中给二维数组分配一个二维数组

3
我有一个函数,它试图循环遍历一个包含在结构体中的二维数组:
typedef struct node
{
    int grid[3][3];
} Node;


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

当我尝试编译这个时,我得到了一个

mp.c:42: 错误:无效的初始化器


有一件事,既然它只是一个只有一个成员的结构体,为什么不直接传递一个int[][3]呢? - Jesus Ramos
@Jesus Ramos 我计划在结构体中添加其他属性。 - Jeune
1
@Jesus Ramos:尽管Jeune说不会这样,但将数组作为“一等公民”类型是一种常见的方式。这样做可以带来许多好处,例如允许赋值(自动复制数组)或从函数返回数组。 - sidyll
好的,只是确保你不会让自己过于复杂 :) - Jesus Ramos
你应该尝试理解数组和指针之间的区别。 - Stan
3个回答

11
在C语言中,你不能直接赋值给数组。这是不允许的。当你写下以下代码时:
int grid[3][3] = node->grid;

你试图从传入的node初始化本地数组grid。如果被允许(实际上是不行的),那么之后就不需要循环了。

不过,即使结构体包含数组,你仍然可以进行赋值,所以如果本地结构体是一个Node,你可以这样写:

Node local = *node;
你不需要在之后循环数组来初始化local。你可以循环数组,一次复制一个元素:
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
        grid[i][j] = node->grid[i][j];

你也可以使用memmove()memcpy()

int grid[3][3];

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

曾经,另一个回答建议:

将这行代码更改为:

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

收件人:

int **grid = node->grid;
我注意到这段代码行不通,被正当地要求解释原因。这需要用到空间和格式。

首先,编译器提示:
warning: initialization from incompatible pointer type

这句话的意思是“你正在玩火”,假设我们忽略了这个警告,本地的grid现在指向数组的左上角(如果您看到数组从左到右增长),存储在那里的值是一个普通的数字,而不是初始化的指针。但是当编译器评估grid[0]时,它被迫假设它将生成一个指针。如果node->grid[0][0]包含零,您可能会因为解除了空指针引用(在假定指针和int大小相同的情况下,这通常适用于32位系统)而得到分段错误和核心转储,或者一些其他未定义的行为。如果node->grid[0][0]包含另一个值,则行为仍然未定义,但不太可预测。


然后我可以像普通的二维数组一样循环遍历网格吗? - Jeune
@Jeune:是的,它只是一个初始化数组,并复制了其他内容,您可以在任何需要的情况下进行操作。这适用于使用哪种(可编译)机制来初始化本地数组。 - Jonathan Leffler
@Jeune:#include <string.h>提供了memcpy()的声明。 - Jonathan Leffler
你的第三个代码片段格式很有趣。非常Python风格 :P - sidyll
1
可以使用int (*grid)[3] = node->grid,在这种情况下可能是最合适的。 - caf
显示剩余2条评论

1
如果不想复制数组,只需要在结构体中获取指向该数组的指针(注意:如果给 *pointer 赋值,则结构体中的数组内容将会被更改),有两种方法可以实现这个目标:
#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;
}

以上代码展示了一个使用指针的简单函数。它们都是安全且符合标准的。

如果要使用指向指针的指针,int**,您需要以不同的方式进行处理,因为数组是在内存中的线性存储(所以上述代码可以使用int*指向数组的开头并对其进行操作),但int** 不行。

编辑

所以这里来了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);
}

您可以通过使用&node->grid[0][0](或者只是node->grid[0])来初始化int *值,从而避免强制转换。 - caf
@caf:谢谢。你说得对。这是我的坏习惯。我懒得考虑类型问题 :-p - Stan

0

1
不行,那样做不起作用。要使其工作,grid[0]grid[1]grid[2]中的每一个都必须是指向数组的指针,尽管看起来并非如此。在node->grid中分配的地址被赋给了grid,但grid[1]没有得到正确的初始化。很难在答案中解释这个问题;在评论中完全是非平凡的。 - Jonathan Leffler
我收到了这个警告:mp.c:42: warning: initialization from incompatible pointer type,这是什么意思? - Jeune
查看此线程以获取更多信息:https://dev59.com/m3NA5IYBdhLWcg3wNa6T - Jonathan DeCarlo
我将更改我的答案以回复另一个重复的帖子。@Jonathan:谢谢你的信息。 - Jonathan DeCarlo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接