关于c ++:当一个函数有一个特定大小的数组参数时,为什么它被一个指针替换?

When a function has a specific-size array parameter, why is it replaced with a pointer?

给定以下程序,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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;
}

输出

1
2
main() 100
foo() 4

  • 为什么数组作为指向第一个元素的指针传递?
  • 它是C的遗产吗?
  • 标准怎么说?
  • 为什么C++的严格类型安全性下降了?
    • 在这些情况下,我总是使用STD::数组,防止必须处理像这样的问题,也使用STD算法。
    • 什么严格的类型安全?谁承诺过严格的类型安全?C++中没有这样的东西。
    • 下面的答案是:数组在传递给函数时成为指针,所以当检查它们的大小时,得到的只是指针的大小。如果你只使用C,我建议你预先计算出数组中作为另一个参数的大小。
    • 相关Linus Rant


    是的,它是从C继承的。函数:

    1
    void foo ( char a[100] );

    将参数调整为指针,因此变为:

    1
    void foo ( char * a );

    如果希望保留数组类型,则应传递对数组的引用:

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

    C++ 03 83.5/3:

    ...The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type"array of T" or"function returning T" is adjusted to be"pointer to T" or"pointer to function returning T," respectively....

    要解释语法:

    在Google中检查"右-左"规则;我在这里找到了一个描述。

    它将大致应用于这个示例,如下所示:

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

    从标识符"A"开始

    'a' is a

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

    'a' is a reference

    &之后,我们到达开口的(处,所以我们再次反向看向右边。我们现在看到了[100]

    'a' is a reference to an array of 100

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

    'a' is a reference to an array of 100 chars

    • 后者的缺点是将数组大小硬连接到函数签名中。函数模板可以避免这种情况。
    • 在一个有点关联的注释中,有人能为我清除上面的语法吗?显然我遗漏了一些东西,但我不太明白它是如何计算对数组的引用的;它看起来更像一个引用数组。
    • 值得一提的是,使用STD::向量将整齐地回避与传递数组有关的所有问题。
    • 这归结为一个事实,即C/C++中的简单数组参数是虚构的——它们实际上是指针。数组参数应该尽可能避免-它们实际上只是混淆了问题。
    • 只是吹毛求疵:函数参数不会衰减为指针。它被调整为指针。如果函数参数是指针,用作函数参数的数组名可以衰减为指针。
    • 太棒了,因为链接现在是404。
    • @萨马拉斯是个不错的接球手。我已经修好了。相应章节为2.4。
    • @理查德科登,链接现在工作。但是,我非常喜欢你的解释,所以我觉得当时不需要阅读链接。谢谢你的回答。


    对。在C和C++中,不能将数组传递给函数。就是这样。

    你为什么要做普通数组?你看过boost/std::tr1::array/std::arraystd::vector吗?

    但是,请注意,可以将对任意长度数组的引用传递给函数模板。从我的头顶上:

    1
    2
    3
    4
    5
    6
    template< std::size_t N >
    void f(char (&arr)[N])
    {
      std::cout << sizeof(arr) << '
    '
    ;
    }

    • 但您可以传入"对数组的引用"。
    • 只是一些遗留代码
    • @理查德:你写评论的时候我刚加上这个。:)
    • 传递对数组的引用不限于"函数模板"。可以通过引用非模板函数来传递数组。使用函数模板的优点是可以推导数组索引,从而允许您为不同大小的数组类型调用函数。
    • 如果我将数组放入一个结构并将其传递给函数,它将报告相同的大小。所以将C数组和对象作为参数传递的规则是不同的?
    • @CStamas,是的,在C中传递数组和对象的规则是不同的。当作为参数传递时,结构实际上是按值复制的。数组被视为指向其第一个元素的指针。(C中的数组和指针是非常相关的。它们并不相同,但为了传递参数,它们是相同的)
    • @cstomas:在8.3.5/3中,标准描述了在声明函数时应用于参数的规则。这些规则决定函数是重新声明还是重载。本节明确说明了参数类型是数组(或函数)的位置,这就是数组和结构情况之间行为不同的原因。
    • @理查德:我知道。(另请参见我对stackoverflow.com/questions/1328223/&hellip;的评论)但是,由于问题是关于传递数组大小,我想cstamas希望传递任意长度的数组。我编辑了我的答案来强调这一点。
    • 或者只是std::cout << N;
    • 现在有STD:数组。
    • @特雷弗谢谢你,补充说。


    在C/C++术语中有一个宏伟的词,用于静态数组和函数指针-衰变。考虑以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

    • 还有?这充其量只能间接地说明发生了什么。操作人员问为什么语言是这样设置的。另外,"静态数组"是一个令人困惑的术语,当您真正的意思是动态分配时;从技术上讲,您所显示的数组具有extern链接,而不是static链接。我不知道函数指针和这里的任何东西有什么关系?