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

How C++ compiler knows the length of an array

本文关键字:C++ 长度 数组 何知道 编译器      更新时间:2023-10-16

例如,在main函数中,我可以写int arr[42],并且我可以使用范围为循环而不指示其长度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]知道数组的长度。我说的对吗?当我使用循环范围并学习模板时,我遇到了这个问题。我以前在使用 C 时没有遇到过这样的问题。

数组

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

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

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的行为类似于数组,因为它是对数组类型的引用。

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

对于类类型,成员函数 beginend (如果存在)用于确定端点,否则将使用非成员重载、beginend

beginend,而不是std::beginstd::end,因为标准库中beginend的通用用法是这样的:

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

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

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

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

foo.c:

#include <stdio.h>
extern int a[];
int main()
{
    printf("%dn", sizeof(a));
}

酒吧:

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[]' 

每当你通过引用传递数组时,你都可以推断出它的大小(记住数组不是指针)。因此,基于范围的 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,因为编译器在编译时知道数组的大小(如 @T.C 所述)。所以上面的代码仅用于演示目的;)

有关基于范围的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是数组类型,则分别__rangebegin-expr__range+ __bound,其中 __bound 是数组绑定。如果_RangeT是未知大小的数组或不完整类型的数组,则程序格式不正确;