通过指向不完整数组类型的指针,将多维数组传递给没有最大尺寸的函数

Grz*_*ski 4 c c11

从我记得的数组总是作为指针传递.例如,声明:

void foo(int array[2][5]);
Run Code Online (Sandbox Code Playgroud)

编译器的意思与完全相同:

void foo(int (*array)[5]);
Run Code Online (Sandbox Code Playgroud)

你可以说这两种形式都是等价的.现在,我想知道,为什么允许它声明为:

void foo(int (*array)[]);
Run Code Online (Sandbox Code Playgroud)

而不是:

void foo(int array[][]);
Run Code Online (Sandbox Code Playgroud)

举个例子:

#include <stdio.h>

void foo(int (*p)[3]);
void bar(int (*p)[]);

int main(void)
{
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};

    foo(a);
    bar(a);

    return 0;
}

// The same as int p[][3] or int p[N][3] where N is a constant expression
void foo(int (*p)[3]) 
{

}

// Would it the same as int p[][] or int p[N][] (by analogy)?
void bar(int (*p)[])
{

}
Run Code Online (Sandbox Code Playgroud)

它编译很好,没有警告,但如果我将bar声明改为:

void bar(int p[][]);
Run Code Online (Sandbox Code Playgroud)

那是一个错误.

为什么C允许这种"模糊"的方式传递数组?

Iha*_*imi 5

数组不是指针,如果你声明一个具有未指定大小的指针数组就可以了,因为它将存储在poitners中存储的地址,并且每个元素的大小都是已知的,但是p[][]要求数组是连续的而不是它们的地址和数组的大小是未知的,所以这就是问题所在.

要说清楚,如果你说int p[][]你不知道距离多远p[0],p[1]int (*p)[]你知道距离是指针的大小.

数组转换为指针,但不是指针.

  • @Purag使用较新版本的C标准,您可以传递大小(在其他参数中)以在运行时执行此操作...例如.`void arrfunc(int x,int y,int arr [y] [x])` (2认同)
  • 实际上,使用`int(*p)[]`你*不知道距离是指针的大小 - 这是一个指向`int`数组的指针,你省略了那些阵列.你的意思是`int*p []`(指向`int`的指针数组)? (2认同)

Wou*_*lst 5

数组可用作指针,但它们不仅仅是指针; 他们指出了一些有大小的东西.这样,您可以告诉编译器您对数组的第三个或第五个元素感兴趣,并且它将通过将一个元素的大小乘以三或五来计算该元素的位置以查找偏移量,并添加该元素偏移到数组的地址.

C实际上没有多维数组.它具有的是数组数组,这几乎是一回事.说你有这个:

int a[4][5];
Run Code Online (Sandbox Code Playgroud)

在内存中,这将是这样的:

  [0]                 |[1]                |[2]                |[3]
  +-------------------|-------------------|-------------------|-------------------+
a |   |   |   |   |   |   |   |   |   |   |   |   | X |   |   |   |   |   |   |   |
  +-------------------|-------------------|-------------------|-------------------+
    0   1   2   3   4 | 0   1   2   3   4 | 0   1   2   3   4 | 0   1   2   3   4 |
Run Code Online (Sandbox Code Playgroud)

然后您尝试访问索引处的元素[2][2],然后系统需要执行以下计算(概念上):

  • 获取int的大小,并将其乘以5以获得内部数组的大小
  • 获取该内部数组的大小,并乘以2以获得外部数组中第二个元素的偏移量
  • 再次获取int的大小,并将其乘以2以获得内部数组中第二个元素的偏移量
  • 添加两个偏移量; 那是你的记忆地址

在这种情况下,计算是*(a + (2 * 5 * sizeof(int)) + (2 * sizeof(int))),给出*(a + 12*sizeof(int)),这确实是与起始指针的正确偏移.

所有这些意味着需要定义内部数组的大小,以便编译器能够进行该计算.如果没有定义多维数组的任何维度(但最左边的维度)的大小,那么您没有定义的大小,编译器将会犹豫不决.