【问题标题】:Multi dimensional array reference in struct in cc中struct中的多维数组引用
【发布时间】:2017-09-03 17:54:31
【问题描述】:

如果我有这样的多维数组:

int arr2d[2][3] = {{0,1,2}, {10,11,12}};

我可以将它传递给这样的函数:

void foobar(int arg[][3])

这不是按值调用,而是按引用调用,所以只是一个指向起始地址的指针,但编译器仍然知道它是一个二维数组,我可以像在函数中一样访问它。

现在如何在结构中工作?

typedef struct {
  int arr2d[][3];
} Foobar_t

首先这给了我:error: flexible array member in otherwise empty struct。我可以通过这样做来解决这个问题:

typedef struct {
  int dummy;
  int arr2d[][3];
} Foobar_t

它将在没有错误或警告的情况下编译。但是当我尝试像Foobar_t foobar = {1337, arr2d} 一样使用它时,我收到了一些警告:

missing braces around initializer
initialization makes integer from pointer without a cast

访问时:subscripted value is neither array nor pointer nor vector

一维数组可以很容易地被视为指针。但是对于多维数组,编译器需要知道不同维度的大小才能正确计算偏移量。有没有不强制转换(int (*)[3])的方法,为什么语法与函数参数不同?

所以这是我想避免的解决方法:

#include <stdio.h>

static int testArr[2][3] = {{0,1,2},{10,11,12}};

typedef struct {
  int *arr2d;
} Foobar_t;


int main( int argc, char** argv ) {
  Foobar_t foobar = {(int*)testArr};
  int (*arr2d)[3] = (int (*)[3]) foobar.arr2d;
  printf("testStruct_0_0: %d\n", arr2d[0][0]);
  printf("testStruct_1_0: %d\n", arr2d[1][0]);

  return 1337;
}

编辑:

一些 cmets 建议引用不是正确的词。当然,在 C 语言中,这是通过指针实现的。 所以这个问题的 TLDR 是:指向多维数组的指针类型的语法是什么样的。

在我的解决方法代码中已经可以看到答案。就是这样,继续前进,这里没什么可看的;)不过还是感谢您的回复。

【问题讨论】:

  • C中没有引用
  • 您不能在结构中包含不完整的类型,除非结构末尾的 FAM 具有至少一个其他成员(因此您的 dummy 成员)。而且,当您拥有 FAM 时,您必须动态分配具有正确内存量的结构。
  • 在您的解决方法中,您的行 Foobar_t foobar = {(int*)testArr}; 可以更简洁地编写为 Foobar_t foobar = { &amp;testArr[0][0] }; — 无需强制转换。第二个演员不是那么容易避免的。
  • @Filip Kočica:如果C中没有引用,那么dereference operator *是干什么用的?

标签: c arrays pointers struct reference


【解决方案1】:

C 语言中没有“引用调用”。函数参数总是按值传递。数组确实看起来很特别,因为在大多数表达式(包括函数调用)中,数组衰减为指向其第一个元素的指针。这意味着当一个数组被用作函数调用的参数时,指向第一个元素的指针被传递给函数而不是数组;但是传递的是这个指针的值。

在函数声明器中,数组类型被调整为指向适当类型的指针。这是特定于函数声明符的语义。因此,函数声明如下:

void foobar(int arg[][3]);

调整为将指向三个ints 的数组的指针作为参数:

void foobar(int (*arg)[3]);

一般来说,int arg[][3] 这样的类型表达式是一个不完整类型,因为没有更多信息就不可能知道数组arg[][] 的大小。

C 中的结构不允许使用不完整类型指定成员类型(有一个例外),因为没有此信息就无法知道struct 的大小。此外,struct 说明符不会像函数声明符那样对数组类型进行同样的调整,因为structs 实际上可能包含数组成员。

structs 中不完整类型规则的例外是灵活的数组成员struct 中至少有两个命名成员的最后一个成员可能具有不完整的数组类型。

问题中问题的简单解决方案是将struct 的说明符更改为使用指向数组的指针。注意这里的成员.arr2d不是一个数组,而是一个指向三个ints的数组的指针:

typedef struct {
  int (*arr2d)[3];
} Foobar_t;

【讨论】:

  • Call-by-reference 表示数据由引用给出:这里是地址,可以由 * 运算符取消引用(或对某些类型使用 -> 运算符隐含)。
  • 感谢您的回答。 int (*arr2d)[3] 是我正在寻找的。我在发布问题的那一刻就注意到了这一点,因为在我的解决方法中我使用的是那个确切的类型......
  • @cubei-- 我看到你找到了解决方案,但不是int arr2d[][3] 可以在函数声明符中工作但在struct 说明符中工作的原因;这是我回答的主旨。此外,C 没有引用,而是指针。某些语言使用引用,但在 C 中,它始终是传递给函数的值(例如,int 或指向 int 的指针);区别很重要。
  • @cubei--注意数组不是通过引用传递的,因为C语言中没有这样的东西。数组衰减指向函数调用中它们的第一个元素的指针,并且这个指针按值传递。这是在 C 语言中将参数传递给函数的唯一方式;它可以被认为是对数组的模拟引用调用(有时会误导性地认为数组在 C 中是通过引用传递的:它们不是)。
  • 那么什么是不按值传递的引用呢? c++有这样的东西吗?
【解决方案2】:

您可以尝试使用int **arr2d,这将允许您通过arr2d[x][y] 访问它,或者简单地将其转换为像int *arr2d = malloc(2*3*sizeof(int)); 这样的一维数组。 这样,您需要访问如下值:arr2d[x*m + y];,其中xy 与前面的示例相同,而m 是行的大小。

我还建议您将二维数组的行号和列号存储到结构中。

【讨论】:

  • 如果类型是int**并通过arr2d[x][y]访问,那么编译器怎么知道y的偏移量是1个int,x的偏移量是3个int?
  • 它根本不知道这些;事实上,您应该将额外的参数传递给“手动”检查您是否不会超出任何大小。但请记住,在使用 int** 变量时,您应该手动初始化每个变量,如下所示:int **arr2d;//n = # of rows, m = # of columns --&gt; allocate n*m "places"arr2d = (int **)malloc(n * m * sizeof(int));//for each row i, allocate m "places"for(int i = 0 ; i &lt; n; i++){arr2d[i] = (int *)malloc(m * sizeof(int));}
  • 如果它不知道,那么通过arr2d[x][y] 的访问是不可能的,除非如上所述进行强制转换。 malloc 的东西与 questen 无关。因为据说该数组已经可用,应该只是引用。
【解决方案3】:

原来只是看一下变通方法就向我展示了解决方案:

typedef struct {
  int (*arr2d)[3];
} Foobar_t;

这是指向二维数组的指针的正确类型。 type (*name)[n] 也适用于函数参数。

那么为什么其他语法type name[][n] 对函数参数仍然有效?可能与结构的灵活数组特性的冲突使其无法在那里工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    • 2014-04-11
    • 2016-07-16
    • 2015-07-08
    • 2013-03-21
    • 2014-06-20
    • 2016-12-07
    相关资源
    最近更新 更多