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

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

其他答案描述了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编程语言(C当代)所做的。

好吧,大多数在数​​组上运行的函数对于所有可能的数组长度都是一样的,所以指定大小只会损害你重用它们的能力。

例如,为什么呢

 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 

至于省略多个维度,在使用普通多维数组时这是不可能的(因为你需要知道维度以了解第一行何时结束而第二行何时开始)。 但是,如果您愿意为临时空间花费一些(少量)额外内存,则完全可以使用指针代替指针: http : //www.eskimo.com/~scs/cclass/int/sx9b.html

在声明中

实际上,你不能完全忽略最右边或最左边的尺寸。

但是,如果您有初始化程序,则只能为您推断最左侧。

在函数参数列表中

当您通过值将数组传递给函数时,实际上是将指针传递给该数组的第一个元素。 是的,它看起来就像你传递数组一样,但是,不,你不是。

考虑:

 void f(int ar[3]) void f(int ar[]) 

两者都是等效的语法混淆:

 void f(int* ar) 

没有数组的痕迹,更不用说三个元素之一了。

现在:

 void f(int ar[][3]) 

对于等效语法,这是令人困惑的语法:

 void f(int (*ar)[3]) 

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

总之,不要过分关注类似于[]的类似数组的语法; 它并不能代表真正发生的事情。

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

这与你的问题有什么关系?

假设以下代码行:

 int arr[10] = {0,1,2,3,4,5,6,7,8,9}; foo(arr); 

我们将数组表达式arr作为参数传递给foo 。 由于arr不是sizeof&的操作数,因此其类型从“10元素数组int ”隐式转换为“指向int ”。 因此,我们将指针值传递给foo ,而不是数组。

事实certificate,在函数参数声明中, T a[]T a[N]T *a同义词; 所有三个都声明a 指向 T指针 ,而不是T的数组。

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

 void foo(int *a) // <- foo receives a pointer to int, not an array 

要么

 void foo(int a[]) // <-- a[] is a synonym for *a 

两者都意味着同样的事情; 都声明a指向int的指针。

现在让我们看一下多维数组。 假设以下代码:

 int arr[10][20]; foo(arr); 

表达式arr具有类型“20元素数组的10元素数组”。 根据上述规则,它将隐式转换为“指向20个元素的int数组的指针”。 因此, foo的原型定义可以写成

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

要么

 void foo(int a[][20]) // <-- a[][20] is a synonym for (*a)[20] 

同样,两者都声明a 指针 ,而不是一个数组。

这就是为什么你可以在函数参数声明中删除最左边(也是最左边)的数组索引。