C++ 为什么是c/c++;允许在函数调用中省略多维数组最左边的索引?

C++ 为什么是c/c++;允许在函数调用中省略多维数组最左边的索引?,c++,c,compiler-construction,language-design,C++,C,Compiler Construction,Language Design,我只是想知道,在将多维数组传递给函数时,为什么允许省略多维数组最左边的索引?为什么不止一个索引?编译器如何找出省略了一个索引的大小?在声明中 实际上,不能完全忽略最右边或最左边的维度 然而,只有当您有一个初始化器时,才能为您推导出最左边的值 在函数参数列表中 当按值将数组传递给函数时,实际上是在传递指向该数组第一个元素的指针。是的,从语法上看,你传递的是一个数组,但不是 考虑: void f(int ar[3]) void f(int ar[]) 这两种语言的语法都比较混乱: void f(

我只是想知道,在将多维数组传递给函数时,为什么允许省略多维数组最左边的索引?为什么不止一个索引?编译器如何找出省略了一个索引的大小?

在声明中

实际上,不能完全忽略最右边或最左边的维度

然而,只有当您有一个初始化器时,才能为您推导出最左边的值

在函数参数列表中

当按值将数组传递给函数时,实际上是在传递指向该数组第一个元素的指针。是的,从语法上看,你传递的是一个数组,但不是

考虑:

void f(int ar[3])

void f(int ar[])
这两种语言的语法都比较混乱:

void f(int* ar)
void f(int (*ar)[3])
没有数组的痕迹,更不用说三个元素中的一个了

现在:

这是一种令人困惑的等效语法:

void f(int* ar)
void f(int (*ar)[3])
其中
int(*)[3]
是指向数组第一个元素的指针类型(指向
int[3]


总之,不要太注意类似数组的语法,它看起来像
[]
;它并不真正代表真正发生的事情。

除非它是
sizeof
或一元
&
运算符的操作数,或者是用于在声明中初始化数组的字符串文字,否则类型为“N-element array of
T
”的表达式将隐式转换为“指向
T
的指针,并将计算为数组中第一个元素的地址

这些和你的问题有什么关系

假设以下代码行:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
foo(arr);
我们将数组表达式
arr
作为参数传递给
foo
。由于
arr
不是
sizeof
&
的操作数,其类型被隐式地从“int的10元素数组”转换为“指向int的指针”。因此,我们将指针值传递给
foo
,而不是数组

事实证明,在函数参数声明中,
ta[]
ta[N]
ta*a
的同义词;这三个函数都将
a
声明为指向
T
的指针,而不是
T
的数组

我们可以将
foo
的原型定义编写为

void foo(int *a)     // <- foo receives a pointer to int, not an array
表达式
arr
的类型为“10元素数组/20元素数组/int”。根据上述规则,它将隐式转换为“指向
int
的20个元素数组的指针”。因此,
foo
的原型定义可以写成

void foo(int (*a)[20])  // <-- foo receives a pointer to an array, not an array of arrays

void foo(int(*a)[20])/其他答案描述了C标准如何处理数组到指针的转换,以及这如何影响函数声明,但我觉得他们没有讨论为什么,所以我来看看

在C语言中,数组代表内存中紧密压缩的元素

A -> _ _ _ _ _ _ ...
i:   0 1 2 3 4 5 ...
在上面的示例中,每个数组元素的宽度为1。为了找到第i个元素,我们必须找到第i个地址。(请注意,最左边的尺寸(大小)在这里并不重要)

现在考虑多维数组:

B -> [_ _ _][_ _ _][_ _ _][_ _ _]...
i:    0 0 0  1 1 1  2 2 2  3 3 3
j:    0 1 2  0 1 2  0 1 2  0 1 2
     ^first row    ^third row
要找到
A[i][j]
的偏移量,我们需要跳过i行(3*i),然后跳过j元素->(3*i+j)。请注意,这里也不需要第一个维度的大小

现在应该很清楚,使用数组时不需要最左边的大小,只有在创建数组时才需要


既然不需要给出最左边索引的维度,那么出于完整性考虑,为什么不给出它呢?毕竟,这是在Pascal编程语言(a C)中完成的

大多数在数组上运行的函数在所有可能的数组长度上都是一样的,因此指定大小只会影响重用它们的能力

比如,你为什么要这样做

int sum(int arr[10]){
    int s = 0, i;
    for(i=0; i<10; i++){
        s += arr[i];
    }
    return s;
}
int和(int arr[10]){
int s=0,i;

对于(i=0;i你能提供一些代码作为例子吗?“‘数组’作为多维数组的声明必须对所有维度都有边界,除了第一个”第一个不能的证据在哪里?……啊。我隐约记得,如果它只有一个维度,而你将其留空,它会被视为一个指针。
void foo(int bar[][3] );
?@Philip:Pfft,关于他传递给函数的部分是对问题的编辑。我会补充我的答案。@Tomalak:没有注意到OP的编辑。我当然不是想暗示你什么,我只是好奇。@Mark:你在想什么时候函数有一个参数
int ar[]
;但是,这与声明无关。:)您的绘图帮助很大。非常感谢
B -> [_ _ _][_ _ _][_ _ _][_ _ _]...
i:    0 0 0  1 1 1  2 2 2  3 3 3
j:    0 1 2  0 1 2  0 1 2  0 1 2
     ^first row    ^third row
int sum(int arr[10]){
    int s = 0, i;
    for(i=0; i<10; i++){
        s += arr[i];
    }
    return s;
}
int sum(int arr[], int n){
    int s = 0, i;
    for(i=0; i<n; i++){
        s += arr[i];
    }
    return s;
}