我可以在C99中使用数组作为函数参数吗?

时间:2013-09-16 16:42:53

标签: c arrays c99

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

  

参数声明为''数组类型''应调整为''限定指针              type'',其中类型限定符(如果有)是在[和]中指定的类型                 数组类型推导。

我理解为:

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

然而,即使用gcc -Wall -Werror -std=c99 -pedantic-errors编译,gcc 4.7.3也会很乐意接受这两个函数定义。由于我不是C专家,我不确定是否可能误解了标准的含义。

我也注意到了

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

将始终返回sizeof(int *)而不是数组大小,这使我认为int arr[]被处理为int *而gcc只是试图让我感觉更舒服。

有人可以解释这个问题吗?仅供参考,此问题来自this comment

2 个答案:

答案 0 :(得分:3)

某些背景信息:

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

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

这一切都很好,但为什么T被允许作为指针声明?我不能说这就是肯定的原因,但我怀疑它是B语言的延续,从中衍生出C语言。实际上,几乎所有关于C中数组的一切都是暗淡的或不直观的是B的保留。

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

arr[]

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

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

arr

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

  1. 大多数从事C初始版本工作的人都熟悉它;
  2. 它强调该参数表示调用者中的数组;
  3. 就个人而言,我更倾向于Ritchie使用ptr来指定各处的指针,但他没有(或者,使用*来指定所有上下文中的指针,而不仅仅是函数参数声明)。我通常会建议每个人都使用[]表示函数参数而不是*,因为它更准确地传达了参数的类型,但我可以理解为什么人们更喜欢第二种表示法。

答案 1 :(得分:2)

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

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

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