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

发布于 2024-11-02 14:20:51 字数 71 浏览 2 评论 0原文

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

I was just wondering why it is allowed to omit the leftmost index of a multidimensional array when passing the array to a function ? Why not more than one indexes? And how does the compiler find out the size with one index omitted ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

止于盛夏 2024-11-09 14:20:51

其他答案描述了 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<n; i++){
        s += arr[i];
    }
    return s;
}

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

Other answers described how the C standard handles array to pointer conversion and how this affects the function declaration, but I feel they didn't go into the why, so here I go...

In C, arrays stand for tightly packed elements in memory.

A -> _ _ _ _ _ _ ...
i:   0 1 2 3 4 5 ...

In the above example, each of the array elements is 1 _ wide. To find the i-th element, we thus have to go to the i-th address. (Note that the leftmost dimension (the size) doesn't matter here)

Now consider a multidimensional array:

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

To find the offset of A[i][j] we need to jump over i rows (3*i) and then over j elements -> (3*i + j). Note how the size of the first dimension is also not needed here.

It should be clear by now then, that the leftmost size isn't needed when using the array, it is only needed when you create it.


Since there is no need to give the dimension of the leftmost index, then why not give it anyway, for completeness sake? After all, this is what is done in the Pascal programming language (a C contemporary).

Well, most functions that operate on arrays work the same for all possible array lengths, so specifying the size would only hurt your ability to reuse them.

for example, why do

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

When you can do this instead:

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

As for omitting more then one dimension, this isn't possible when using normal multidimensional arrays (because you need to know the dimension to know when the first row ends and the second starts). However, if you are willing to spend some (little) extra memory for scratch space, it is perfectly possible to use pointers to pointers instead: http://www.eskimo.com/~scs/cclass/int/sx9b.html

扶醉桌前 2024-11-09 14:20:51

在声明中

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

但是,如果您有初始化器,则可以为您推导出最左边的唯一

在函数参数列表中

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

考虑一下:

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]int[3]代码>)。

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

In a declaration

Actually, you can't leave out the rightmost or the leftmost dimension entirely.

However, the leftmost only can be deduced for you if you have an initialiser.

In a function argument list

When you pass an array into a function by value, you're actually passing a pointer to the first element of that array. Yes, it looks from the syntax like you're passing an array but, no, you're not.

Consider:

void f(int ar[3])

void f(int ar[])

Both are confusing syntax for the equivalent:

void f(int* ar)

No trace of an array, let alone one of specifically three elements.

Now:

void f(int ar[][3])

This is confusing syntax for the equivalent:

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

where int (*)[3] is the type of a pointer to the first element of your array (pointer to int[3]).

In conclusion, don't pay too much attention to the array-like syntax that looks like []; it's not really representative of what's truly happening.

决绝 2024-11-09 14:20:51

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

-T 的元素数组”将其类型隐式转换为“指向 T 的指针”,并将计算结果 与您的问题有关?

假设以下几行 code:

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

我们将数组表达式 arr 作为参数传递给 foo,因为 arr 不是 sizeof 的操作数。 > 或 &,其类型从“int 的 10 元素数组”隐式转换为“指向 int 的指针”。正在将指针值传递给foo,不是数组 事实证明,

在函数参数声明中,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 的类型为“10-element array of 20-element array of 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])  // <-- a[][20] is a synonym for (*a)[20]

再次声明,a 都声明为指针,而不是数组。

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

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize an array in a declaration, an expression of type "N-element array of T" will have its type implicitly converted to "pointer to T and will evaluate to the address of the first element in the array.

What does any of that have to do with your question?

Assume the following lines of code:

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

We pass the array expression arr as an argument to foo. Since arr is not an operand of either sizeof or &, its type is implicitly converted from "10-element array of int" to "pointer to int". Thus, we are passing a pointer value to foo, not an array.

It turns out that in a function parameter declaration, T a[] and T a[N] are synonyms for T *a; all three declare a as a pointer to T, not an array of T.

We can write the prototype definition for foo as

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

or

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

Both mean the same thing; both declare a as a pointer to int.

Now let's look at multidimensional arrays. Assume the following code:

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

The expression arr has type "10-element array of 20-element array of int". By the rule described above, it will implicitly be converted to "pointer to 20-element array of int". Thus, the prototype definition for foo can be written as

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

or

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

Again, both declare a as a pointer, not an array.

This is why you can drop the leftmost (and only the leftmost) array index in a function parameter declaration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文