C ++编译器如何知道数组的长度

时间:2015-01-26 00:19:39

标签: c++ c++11

例如,在main函数中,我可以编写int arr[42],我可以使用range for循环而不指示其长度for (auto i : arr)。它运作良好。编译器会知道它的长度。

如果我将此数组传递给另一个函数,例如void foo(int arr[]),我就不能使用range for循环,因为编译器不知道它的长度。

毕竟,如果我有这个模板template<class T, size_t N> void arr_init(T (&foo)[N]),我可以通过arr_init(x)来调用它。显然,编译器调用arr_init(int (&)[42])

我猜编译器通过声明int[42]知道数组的长度。我对吗?当我使用range for loop并学习模板时,我有这个问题。当我使用C时,我还没有遇到过这样的问题。

3 个答案:

答案 0 :(得分:2)

数组类型T[N]与类型T[M]的数组类型T以及两个不同大小NM不同。因此,数组的长度内置在类型中。

OTOH,数组自动decayed指向函数调用参数列表中的指针。换句话说,这三个功能签名之间没有区别:

int func(int []);
int func(int[5]);
int func(int[10]);
int func(int*);

以上所有签名都只是int*。如果要保留数组类型,则需要创建一个以数组引用作为参数的函数。这正是您在示例中template<class T, size_t N> void arr_init(T (&foo)[N])所实现的。在arr_init内,foo的行为类似于数组,因为它是对数组类型的引用。

对于arr类型的数组T[N]range-based for分别使用表达式arrarr+N作为起点和终点。

对于类类型,成员函数beginend(如果存在)用于确定端点,非成员重载失败,beginend使用。

beginend,而不是std::beginstd::end,因为标准中beginend的一般用法图书馆是这样的:

using std::begin;
begin(...);
using std::end;
end(...);

这允许ADL查找用户定义的beginend重载。)

答案 1 :(得分:1)

是的,你是对的。这是因为编译器知道在同一个翻译单元中定义的对象的大小。

事实上,您不需要使用范围for循环来查看类似的行为;你可以使用普通sizeof

foo.c的:

#include <stdio.h>

extern int a[];

int main()
{
    printf("%d\n", sizeof(a));
}

bar.c:

int a[50];

现在测试:

gcc foo.c bar.c
> foo.c: In function 'main':
> foo.c:7:23: error: invalid application of 'sizeof' to incomplete type 'int[]' 

答案 2 :(得分:0)

每当您通过引用传递数组时,您可以推断它的大小(请记住,数组不是指针)。因此,基于范围的for使用的函数std::begin()std::end()可能像数组一样重载

namespace std
{
    template<typename T, std::size_t N>
    T* std::begin(T (&arr)[N])
    {
        return arr;
    }

    template<typename T, std::size_t N>
    T* std::end(T (&arr)[N])
    {
        return arr + N;
    }
}

只有在按值传递时,数组才会衰减为指针,如

void f(int arr[256]){...}

的语法糖
void f(int* arr){...}

PS:看起来基于范围的for不使用std::beginstd::end,因为编译器在编译时知道数组的大小(如@所述) TC)。所以上面的代码仅用于演示目的;)

有关基于范围的for的详细信息,请参阅标准的 6.5.4

  
    

在每种情况下,基于范围的for语句等同于

  
    {
       auto && __range = range-init;
       for ( auto __begin = begin-expr,
                  __end = end-expr;
             __begin != __end;
              ++__begin ) { 
           for-range-declaration = *__begin; 
           statement
       } 
    }
  

6.4.3 / 1 如果_RangeT是数组类型,则 begin-expr end-expr __ range __ range + __bound ,其中 __ bound 是数组绑定的。如果 _RangeT 是未知大小的数组或类型不完整的数组,则该程序格式不正确;