【问题标题】:While loop iterating on too many elements in C++ [closed]虽然循环迭代 C++ 中的太多元素 [关闭]
【发布时间】:2019-10-29 08:44:17
【问题描述】:

当我运行代码时,我得到所有这些数字作为输出,这意味着我的 while 循环似乎正在遍历它不应该越过的元素。为什么会这样? 对于上下文,我目前正在通过 C++ 之旅学习 C++,但我对指针和引用感到非常困惑。

这是我得到代码的地方: Buggy code in "A Tour of C++" or non-compliant compiler?

int counter(int* arr,int c){
    int counter = 0;
    while(*arr){
    cout << *arr<<"\n";
    if(*arr == c){
        ++counter;
    }
    ++arr;
    }
    return counter;
}

int main()
{
    int arr[3] = {1,2,3};
    int count = counter(arr,1);
    cout<< count;
}

示例运行:

/Users/benediktschesch/CLionProjects/untitled/cmake-build-debug/untitled
1
2
3
-945684358
-1153026697
-280532248
32766
1839025881
32767
1839025881
32767
1
Process finished with exit code 0

【问题讨论】:

  • 可能以a good c++ book开头
  • @skeller 那本书是在那个列表中;)(它被列为“入门,有编​​程经验”列表中的第一本书也就不足为奇了)。
  • 这就是我们在 C++ 中使用 std::vector 和朋友的原因,因为正确使用 C 数组要复杂得多。

标签: c++ arrays function count definition


【解决方案1】:

这与不为将用作字符串的字符数组提供空终止符非常相似。

while(*arr) 

表示找到零时停止。

int arr[3] = {1,2,3};

不提供零,因此您无法控制循环何时停止。

TL;DR 解决方案:

使用Library container。 std::vector 或 std::array 非常适合这里,std::count 来自 the &lt;algorithm&gt; library 和 std::begin 和 std::end 来自 the &lt;iterator&gt; library。

#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    int arr[] = { 1, 2, 3 };
    int count = std::count(std::begin(arr), std::end(arr), 1);
    std:: cout << count;
}

说明:

你可以提供一个零

int arr[] = {1,2,3,0};

注意我删除了显式数组大小。不需要,因为编译器从初始化器中的元素数量知道。

还要注意,这将在到达第一个零时停止,所以

int arr[] = {1,2,3,0,1,2,3,0};

只会发现一个 1。这使得零成为一个非常糟糕的值,用于终止整数列表,除非 0 保证不会出现在输入中。

要扫描整个数组并且只扫描数组,需要提供数组的大小。这可以通过传入一个大小参数来完成

int counter(int* arr, size_t len, int c)
{
    int counter = 0;
    while (len--)
    {
        std::cout << *arr << "\n";
        if (*arr == c)
        {
            ++counter;
        }
        ++arr;
    }
    return counter;
}

int main()
{
    int arr[3] = { 1, 2, 3 };
    int count = counter(arr, std::size(arr), 1);
    std:: cout << count;
}

但现代 C++ 中的首选解决方案是使用容器代替数组。容器知道它们的大小,并提供了各种各样的其他工具,使编写代码更容易,更不容易出错。

#include <iostream>
#include <vector>

int counter(const std::vector<int> & arr, int c)
{
    int counter = 0;
    for (const auto & val: arr)
    {
        std::cout << val << "\n";
        if (val == c)
        {
            ++counter;
        }
    }
    return counter;
}

int main()
{
    std::vector<int> arr = { 1, 2, 3 };
    int count = counter(arr, 1);
    std:: cout << count;
}

注意使用range-based for loop 来简化代码。 const auto &amp; val 从arr 和auto 的内容推导出val 的类型。该值不会因循环而更改,因此我们将其声明为const 以防止意外并使其成为参考,因为也许编译器可以执行一些额外的优化巫术。此外,如果容器或容器中的数据类型发生变化,您可以继续重复使用这个确切的语句,而无需更改任何内容。这样可以防止以后在维护代码时出错。

您也可以使用std::array 并将counter 设为模板化函数,用于检测std::array 的大小,但目前这有点多。

下一次演进利用&lt;algorithm&gt; 库。

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    std::vector<int> arr = { 1, 2, 3 };
    int count = std::count(arr.begin(), arr.end(), 1);
    std:: cout << count;
}

在这种情况下,使用迭代器而不是指定长度。这使您可以轻松扫描容器的子集。

这使我们可以利用std::begin 和std::end 将数组转换为一对迭代器,从而重新使用普通数组:

#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    int arr[] = { 1, 2, 3 };
    int count = std::count(std::begin(arr), std::end(arr), 1);
    std:: cout << count;
}

这让我们想到了 TL;DR 解决方案。

【讨论】:

  • 如果有人传入一个数组,其中 0 不是最后一个元素怎么办?
  • 他们有一个令人讨厌的惊喜。我会更新答案。
【解决方案2】:

我建议使用std::array 和range based for loops

#include <array>
#include <iostream>

int counter(const std::array<int, 3> &arr, int c){
    int counter = 0;
    for (auto const a : arr) {
        std::cout << a << "\n";
        if(a == c){
            ++counter;
        }
    }
    return counter;
}

int main()
{
    std::array<int, 3> arr = {1,2,3};
    int count = counter(arr,1);
    std::cout << count;
}

你的问题的原因是在行

while(*arr){

声明

*arr

被评估为布尔值。 *arr == 0 为假,其他情况下为真。在您的代码中,您需要获取数组的大小或值为 0 的最后一个元素。有不同的方法。您可以使用 0 添加最后一个元素,也可以将大小传递给函数。但是 C++ 标准提供了 stl 容器,可以在没有开销的情况下解决您的问题。 std::array 就是这样一个包含数据和数组大小的容器。它是一个模板类,因此大小不需要额外的数据。首先,您应该学习如何使用语言提供的工具,例如 stl 容器和算法。稍后您可以学习如何使用低级函数和数据类型。

【讨论】:

    【解决方案3】:

    除了包含字符串的字符数组之外,所有其他数组(如果您不故意使用标记值)不包含表示数组结束的零元素。因此,如果数组不包含等于 0 的元素(如您的情况),您的函数可以调用未定义的行为。

    所以一般来说,你的函数应该多一个参数来指定数组中元素的数量,应该看起来像

    size_t counter( const int *arr, size_t n, int value )
    {
        size_t count = 0;
    
        for ( const int *p = arr; p != arr + n; ++p )
        {
            if ( *p == value ) ++count;
        }
    
        return count;
    }
    

    并像这样称呼

    int main()
    {
        int arr[] = { 1, 2, 3 };
        const size_t N = sizeof( arr ) / sizeof( *arr );
    
        size_t count = counter( arr, N, 1 );
    
        std::cout << count << '\n';
    }
    

    如果您的编译器支持 C++ 17,那么您可以包含在标头 &lt;iterator&gt; 中声明的标准函数 std::size(),而不是表达式 sizeof( arr ) / sizeof( *arr ),例如 std::size( arr )。

    否则,您可以使用表达式而不是表达式 sizeof( arr ) / sizeof( *arr )

    std::extent<decltype( arr )>::value
    

    前提是包含标头&lt;type_traits&gt;。

    考虑到在标题&lt;algorithm&gt; 中声明的标准算法std::count 执行相同的任务。这是一个演示程序

    #include <iostream>
    #include <iterator>
    #include <algorithm>
    
    int main()
    {
        int arr[] = { 1, 2, 3 };
    
        auto count = std::count( std::begin( arr ), std::end( arr ), 1 );
    
        std::cout << count << '\n';
    }
    

    【讨论】:

    • 在 C++17 之前,我强烈建议创建自己的 std::size 等效项,而不是像 sizeof 那样做危险的事情!
    • @LightnessRacesinOrbit 在 C++ 17 之前有标准类 std::extent.:)
    • 喜欢一个功能,但也可以工作
    猜你喜欢
    • 2014-06-12
    • 1970-01-01
    • 2022-01-15
    • 2017-11-16
    • 2022-01-09
    • 2014-10-11
    • 2018-12-18
    • 1970-01-01
    • 2021-12-23
    相关资源
    最近更新 更多