当一个函数有一个特定大小的数组参数时,为什么会被替换成指针?

74

考虑以下程序:

#include <iostream>

using namespace std;

void foo( char a[100] )
{
    cout << "foo() " << sizeof( a ) << endl;
}

int main()
{
    char bar[100] = { 0 };
    cout << "main() " << sizeof( bar ) << endl;
    foo( bar );
    return 0;
}

输出

main() 100
foo() 4
  1. 为什么数组被传递为指向第一个元素的指针?
  2. 这是从C语言继承来的吗?
  3. 标准规定了什么?
  4. 为什么C++放弃了严格类型安全?

我在这些情况下总是使用std :: array,这可以避免处理此类问题,并且也可以与std算法一起使用。 - paulm
3
严格类型安全是什么?谁承诺了严格类型安全?在C++中并不存在这样的东西。请问需要翻译其他内容吗? - n. m.
以下是答案的 TL;DR:当数组传递到函数中时,它们变成指针,因此当您检查它们的大小时,您得到的只是指针的大小。如果您只使用 C,我建议您预先计算您想从数组中获取的任何大小作为另一个参数。 - Super Cat
相关的Linus愤怒演讲。 - Millie Smith
相关:确定传递给函数的数组大小 - Gabriel Staples
3个回答

92

是的,它是从C语言继承而来的。这个函数:

void foo ( char a[100] );

参数将被调整为指针,并变成:

void foo ( char * a );

如果您希望数组类型得到保留,应该传递数组的引用:

void foo ( char (&a)[100] );

C++ '03 8.3.5/3:

...函数的类型是使用以下规则确定的。每个参数的类型都是从其自己的decl-specifier-seq和declarator中确定的。在确定了每个参数的类型之后,任何类型为“T数组”或“返回T的函数”的参数都将被调整为“指向T的指针”或“指向返回T的函数的指针”,分别....

解释语法:

在google中查找“右左规则”;我在这里找到了一个描述。

它大致应用于此示例如下:

void foo (char (&a)[100]);

从标识符'a'开始

'a'是一个

向右移动-我们找到一个),所以我们反向寻找(。当我们向左移动时,我们经过&

'a'是一个引用

&之后,我们到达开放的(,所以我们再次反向并向右查看。现在我们看到[100]

'a'是一个指向包含100个元素的数组的引用

然后我们再次反向,直到到达char

'a'是一个指向包含100个字符的数组的引用


4
值得一提的是,使用std::vector将会巧妙地避开与传递数组相关的所有问题。 - markh44
5
这归结于C/C++中的普通数组参数实际上是指针这一事实。应尽可能避免使用数组参数-它们只会使事情更加混乱。 - Michael Burr
2
只是挑刺一下:函数参数不会衰减为指针。它被调整为指针。如果函数参数是指针,则用作函数参数的数组名称可以衰减为指针。 - juanchopanza
2
感谢您解释这个规则,因为链接现在是404。 - gsamaras
1
@RichardCorden,链接现在可以使用了。不过,我非常喜欢你的解释,所以我觉得暂时不需要阅读链接。谢谢你的精彩回答。 - gsamaras
显示剩余3条评论

16

是的,在C和C++中,你不能将数组传递给函数。这就是事实。

不过,你为什么要使用普通数组呢?你有没有看过 boost/std::tr1::array/std::array 或者 std::vector

请注意,你可以将任意长度的数组的引用传递给函数模板。

template< std::size_t N >
void f(char (&arr)[N])
{
  std::cout << sizeof(arr) << '\n';
}

7
但是你可以传递“数组的引用”。 - Richard Corden
7
把数组以引用的方式传递不仅限于“函数模板”,也可以用于非模板函数。使用函数模板的好处是可以推导出数组下标,从而允许你为不同大小的数组类型调用该函数。 - Richard Corden
4
@CsTamas,在C语言中传递数组和对象的规则是不同的。当结构体作为参数传递时,实际上是按值进行复制的。而数组则被视为指向其第一个元素的指针。(在C语言中,数组和指针非常相关。它们并不是同一种东西,但在参数传递的目的上,它们是相同的。) - Tyler McHenry
1
现在有std::array了。 - Trevor Hickey
@Trevor 谢谢,已添加。 - sbi
显示剩余4条评论

1
在C/C++术语中,有一个非常棒的词汇用于描述静态数组和函数指针——“decay”。 考虑以下代码:
int intArray[] = {1, 3, 5, 7, 11}; // static array of 5 ints
//...
void f(int a[]) {
  // ...
}
// ...
f(intArray); // only pointer to the first array element is passed
int length = sizeof intArray/sizeof(int); // calculate intArray elements quantity (equals 5)
int ptrToIntSize = sizeof(*intArray); // calculate int * size on your system

4
那又怎样?最多也只是间接暗示了会发生什么。原帖的问题是为什么语言要这样设置。而且,“静态数组”这个术语有点混淆,因为你真正想表达的是动态分配;严格来说,你展示的数组具有“extern”链接性,而不是“static”。我也不确定函数指针在这里有什么关系? - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接