如何为每个" C ++中的循环知道数组的长度

时间:2014-08-10 19:53:08

标签: c++ arrays for-loop foreach

我正在查看http://www.cplusplus.com/doc/tutorial/arrays/中的以下示例,但我无法弄清楚第二个for循环是如何工作的。 for循环如何知道数组何时结束。如果它能弄明白为什么第一个循环不使用类似的方法?我的印象是无法确定阵列的长度。我不确定如何调和这些观念。谢谢!

编辑:感谢所有出色的答案!

#include <iostream>
using namespace std;
int main()
{
  int myarray[3] = {10,20,30};

  for (int i=0; i<3; ++i)
    ++myarray[i];

  for (int elem : myarray)
    cout << elem << '\n';
}

4 个答案:

答案 0 :(得分:5)

这有效的原因是for循环有效 1 使用std::beginstd::end。反过来,这些工作,因为它们专门为内置数组提供重载,如下所示:

template <class T, size_t N>
T *begin(T (&array)[N]) {
    return array;
}

template <class T, size_t N>
T *end(T (&array)[N]) {
    return array+N;
}

尽管在原始(1998)C ++标准发布之前(显然)尚未实现,但该技术并不需要除C ++ 98中提供的任何语言功能之外的任何语言功能。 C ++ 11编写了该技术并将其付诸实施。

因为在这种情况下,参数被指定为对数组的引用,所以只有当参数确实是一个数组时,类型推导才会成功。对于std::begin,还有一些版本支持其他参数类型,并使用(例如)集合的begin()end()成员(如果该类型匹配)。


<子> 1.&#34;有效地&#34;在这种情况下意味着在某些情况下,基于范围的for循环使用beginend,而其他情况则不是。{1}}和begin。如果您进入技术领域,它们不会用于数组,但会直接进行类似的计算。同样,对于具有endbegin(range)成员的容器类型,可以直接使用这些成员。如果这两个都不属实,则使用end(range)std::begin,可以使用std::end / begin(x)end(x) / {{1} }由参数依赖查找找到的对。

答案 1 :(得分:4)

由于数组定义,编译器知道数组的元素数。因此它使用表达式myarraymyarray + 3来遍历数组。

事实上,循环看起来如下

for ( auto first = myarray, last = myarray + 3; first != last; ++first )
{
   auto elem = *first;
   cout << elem << '\n';
}

考虑到基于范围的for语句既不使用std :: begin()也不使用数组的std :: end(),就像其他人在这里写的那样。:)

根据C ++标准

  

- 如果 _RangeT是数组类型,则begin-expr和end-expr为   分别为 __ range __ range + __bound ,其中__bound是数组绑定的。如果_RangeT是一个未知大小的数组或一个数组   不完整的类型,程序是不正确的;

答案 2 :(得分:4)

编译器知道数组的实际类型,并使用基于标准迭代器的循环array(相当于array + 0)和array + length(相当于*(&array + 1))作为范围的各自结束。

对于非数组,它首先在给定范围内查找beginend,然后最终使用ADL查找自由函数。

std::beginstd::end将平均使用ADL查找免费函数,因此,如果您在代码中使用using std::begin;using std::end;,则不合格的名称 - 查找将模仿ranged-for-loop的规则。

来自C ++ 1y的正确部分:

  

6.5.4基于范围的语句[stmt.ranged]

     

1对于表单

的基于范围的for语句
for ( for-range-declaration : expression ) statement
     

range-init等同于括号 90 expression所包围的( expression )以及基于范围的for语句

for ( for-range-declaration : braced-init-list ) statement
     

range-init等同于braced-init-list。在每种情况下,基于范围的for语句等同于

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

其中__range__begin__end是仅为展示定义的变量,_RangeT是表达式的类型,begin-expr和{ {1}}确定如下:

     
      
  • 如果end-expr是数组类型,则_RangeTbegin-expr分别为end-expr__range,其中__range + __bound是数组绑定。如果__bound是未知大小的数组或不完整类型的数组,则该程序格式不正确;
  •   
  • 如果_RangeT是类类型,则在类_RangeT的范围内查找unqualified-ids beginend,就像通过类成员访问查找一样( 3.4.5),如果其中任何一个(或两个)发现至少一个声明,_RangeTbeginexpr分别为end-expr__range.begin();
  •   
  • 否则,__range.end()begin-expr分别为end-exprbegin(__range),其中end(__range)begin在关联的命名空间中查找(3.4.2)。 [注意:不执行普通的非限定查找(3.4.1)。 - 尾注]
  •   
     

2在 for-range-declaration decl-specifier-seq 中,每个 decl-specifier 应为 >类型说明符 constexpr

答案 3 :(得分:3)

你有一种误解,认为远程需要提前知道迭代次数(&#34;长度&#34;)。它没有。

确实需要终止条件,其格式为it != __end __end = x.end()__end = end(x)

当然,数组不能改变它们的大小,所以在这种情况下检测结束并知道长度是等效的(长度可以从开始,结束和指针减法得到)。

正如Oli在评论中提到的,数组的类型确实包含长度信息,而std::end使用数组引用作为参数,以避免衰减丢失此信息的指针。

定义基本上是:

namespace std
{
    template<typename T, size_t N> 
    T* end(T (&array)[N])
    {
        return array + N;
        // or if you want to be cute
        // return 1[&array];
    }
}

当数组引用中的边界是非类型模板参数时,它是可推导的。在编写接受数组的函数时,请使用此功能。

(编译器实际上对数组的内置结构进行了array + N计算,但它并没有使用std::end()。但是无论如何内联后都没有区别,看看你如何能够发挥编译器的相同技巧,这很有用。)