除非它是sizeof
或一元&
运算符的操作数,或者是用于在声明中初始化字符数组的字符串字面量,否则类型为“N元素数组of T
”的表达式将被转换(“衰减”)为类型为“指向T
的指针”的表达式,并且表达式的值将是数组中第一个元素的地址。
当您将数组表达式作为参数传递给函数时:
int arr[100];
...
foo( arr );
函数实际接收的是数组第一个元素的指针,而不是数组的副本。其行为与您编写的代码完全相同:
foo( &arr[0] );
有一个规则,类型为T a[N]
或T a[]
的函数参数会被“调整”为T *a
,因此如果您的函数声明是
void foo( int a[100] )
它将被解释为你所写的一样
void foo( int *a )
这会带来一些重要的后果:
在我的代码中,我不在函数参数列表中使用数组样式声明 - 函数接收的是指针,因此我使用指针样式声明。我可以看到使用数组样式声明的论点,主要是作为文档说明(此函数期望具有此大小的数组),但我认为强调参数的指针性是有价值的。
请注意,指向数组的指针也存在相同的问题 - 如果我调用
foo( &arr );
那么foo的原型需要是这样的
void foo( int (*a)[100] );
但这也是同样的原型,就像我调用它一样
void bar[10][100];
foo( bar );
就像你无法知道参数a
是指向单个int
还是指向一系列int
中的第一个,你也无法知道bar
是指向单个100元素数组还是指向100元素数组序列中的第一个。
这就是为什么在 C99 之后
gets
函数被弃用,并在 C2011 中从标准库中删除的原因 - 没有办法告诉它目标缓冲区的大小,因此它会愉快地写入超出数组末尾的输入并覆盖其后面的任何内容。这就是为什么它成为了如此流行的恶意软件攻击方式。
void f(int (*a)[4]);
和void g(int a[4]);
不是等价的。每个函数中只需使用printf("%p\n", (void*)(a + 1));
。 - pmgsizeof
运算符对于一个维度的工作方式如预期一样,但对于另一个维度则不然。因此,事实上,它可能会非常具有误导性。 - DaBlersizeof
,因此请避免使用它。 - Weather Vane