0

C99 标准在 6.7.5.3/7 中说明如下:

将参数声明为“类型数组”应调整为“类型限定指针”,其中类型限定符(如果有)是在数组类型派生的 [ 和 ] 中指定的那些。

我理解为:

void foo(int * arr) {} // valid
void foo(int arr[]) {} // invalid

然而,gcc 4.7.3 将愉快地接受这两个函数定义,即使使用gcc -Wall -Werror -std=c99 -pedantic-errors. 由于我不是 C 专家,我不确定我是否误解了标准所说的内容。

我也注意到

size_t foo(int arr[]) { return sizeof(arr); }

将始终返回sizeof(int *)而不是数组大小,这坚定了我的信念,int arr[]即被处理为int *gcc 只是想让我感觉更舒服。

有人可以阐明这个问题吗?仅供参考,这个问题来自这个评论

4

2 回答 2

3

一些上下文:

首先,请记住,当类型为“N-element array of T”的表达式出现在它不是sizeof一元运算符的操作数的上下文中&,或者不是用于初始化另一个数组的字符串时声明时,它将被转换为“指针T”类型的表达式,其值将是数组中第一个元素的地址。

这意味着当您将数组参数传递给函数时,该函数将接收一个指针值作为参数;在调用函数之前,数组表达式被转换为指针类型。

这一切都很好,但是为什么arr[]允许作为指针声明呢?我不能说这是肯定的原因,但我怀疑这是 B 语言的遗留物,C 是从 B 语言衍生而来的。事实上,几乎所有关于 C 中数组的奇怪或不直观的东西都是 B 的保留。

B 是一种“无类型”语言;对于浮点数、整数、文本等,您没有不同的类型。一切都存储为固定大小的单词或“单元”,而内存被视为单元的线性阵列。当您在 B 中声明一个数组时,如

auto arr[10];

编译器将为数组留出 10 个单元格,然后留出额外的第 11 个单元格来存储数组第一个元素的偏移量,并且该额外的单元格将绑定到变量arr. 与 C 中一样,B 中的数组索引计算为*(arr + i); 您将获取存储在 中的值arr,添加偏移量i,然后取消引用结果。Ritchie 保留了大部分这些语义,除了不再为指向数组第一个元素的指针留出存储空间。相反,该指针值将在翻译代码时从数组表达式本身计算出来。这就是为什么数组表达式被转换为指针类型的原因,为什么&arrarr给出相同的值,如果不同的类型(数组的地址和数组的第一个元素的地址相同)以及为什么数组表达式不能成为赋值的目标(没有什么可以赋值没有存储有为独立于数组元素的变量预留)。

现在这是有趣的一点;在 B 中,您将“指针”声明为

auto ptr[];

这具有分配单元格以将偏移量存储到数组的第一个元素并将其绑定到的效果ptr,但ptr没有特别指向任何地方;您可以将其分配为指向不同的位置。我怀疑符号被搁置有几个原因:

  1. 大多数开发 C 初始版本的人都熟悉它。
  2. 它有点强调参数代表调用者中的一个数组;

就个人而言,我更希望 Ritchie 曾经*在任何地方指定指针,但他没有(或者,或者,[]在所有上下文中都使用指定指针,而不仅仅是函数参数声明)。我通常会建议大家*对函数参数使用符号而不是[],只是因为它更准确地传达了参数的类型,但我可以理解为什么人们更喜欢第二种符号。

于 2013-09-16T19:52:08.510 回答
2

您的有效无效声明在内部都是等效的,即编译器将后者转换为前者。

您的函数看到的是指向数组第一个元素的指针。

PS。另一种方法是将整个数组推入堆栈,从时间和空间的角度来看,这将是非常低效的。

于 2013-09-16T16:49:44.077 回答