在函数参数声明的上下文中,T a[] 和T a[N] 都被解释为T *a;在所有三种情况下,a 都被声明为指向T 的指针,而不是T 的数组。因此,在int main(int argc, char *argv[]) 中,argv 实际上被声明为char **,或指向char 的指针的指针,而不是指向char 的指针数组。
(edit -- 请注意,仅对于函数参数声明是正确的;对于常规变量声明,T a[N] 和 T a[] 都声明 a作为T 的数组)。
由于它是一个指针值,它可以被赋值,也可以递增。
除此之外,language standard 不得不说:
5.1.2.2.1 程序启动
...
2 如果它们被声明,主函数的参数应遵循以下
约束:
...
— 参数argc 和argv 以及argv 数组指向的字符串应
可由程序修改,并在程序之间保留它们最后存储的值
启动和程序终止。
编辑
这是函数参数的语言:
6.7.6.3 函数声明符(包括原型)
...
7 将参数声明为“type”的数组应调整为“合格的指向
type'',其中类型限定符(如果有)是在 [ 和 ] 的
数组类型推导。如果关键字static 也出现在[ 和] 的
数组类型推导,然后对函数的每次调用,对应的值
实际参数应提供对数组的第一个元素的访问,其中至少有
由大小表达式指定的元素。
编辑2
一些示例(假设是 C99 编译器):
void foo(int a[], size_t len)
{
size_t i;
printf("sizeof a = %zu\n", sizeof a);
printf("sizeof (int *) = %zu\n", sizeof (int *));
for (i = 0; i < len; i++)
printf("a[%zu] = %d\n", i, *a++);
}
int main(void)
{
int a1[5] = {0};
int a2[] = {0, 1, 2, 3, 4};
printf("sizeof a1 = %zu\n", sizeof a1);
printf("sizeof a2 = %zu\n", sizeof a2);
foo(a1, sizeof a1 / sizeof a1[0]);
foo(a2, sizeof a2 / sizeof a2[0]);
return 0;
}
再来一句标准话:
6.3.2.1 左值、数组和函数指示符
...
3 除非它是sizeof 运算符、_Alignof 运算符或
一元 & 运算符,或者是用于初始化数组的字符串文字,该表达式具有
type ‘‘array of type’’ 被转换为类型为 ‘‘pointer to type’’ 的表达式,它指向
到数组对象的初始元素并且不是左值。如果数组对象有
注册存储类,行为未定义。
在函数main中,a1和a2被声明为int的5元素数组; a2 从初始值设定项中的元素数获取其大小。 表达式 a1 和 a2 因此具有类型“int 的 5 元素数组”,它们可能不是赋值表达式的目标,也可能不是 ++ 的操作数或-- 运营商。当这些表达式出现在对foo 的调用中时,它们的类型将根据上述规则转换为“指向int 的指针”。因此foo 接收到a 的指针值,而不是数组值(这包含在将数组参数转换为指针类型的规则中)。所以foo中的表达式a的类型为int *,或者指向int的指针;因此,a 可能是赋值的目标,它可能是++ 和-- 的操作数。
还有一个区别:根据上面引用的规则,当数组表达式是 sizeof 运算符的操作数时,不会发生指针类型的转换; sizeof a1 应计算为数组 a1 (5 * sizeof int) 占用的字节数。然而,由于foo 中的a 的类型为int *,而不是int [5],因此sizeof a 应该只计算指向int 的指针的字节数(sizeof (int *))。