WRITING · 文章

C++ 学习日志 #03|嵌套循环、累加器与指针综合训练

ZRonin

学习方向: C++ / 数据结构基础
今日主题: 嵌套循环、变量初始化、累加与计数、指针遍历、条件判断

一、数字三角形与累加器

今天的第一道练习是使用两层 for 循环输出数字三角形,同时计算每行的数字之和与全部数字的总和。

输入:

4

预期输出:

1  行和:1
12  行和:3
123  行和:6
1234  行和:10
总和:20

1. 最初代码中的问题

最初使用了两个累加器:

int rowSum = 0;
int totalSum = 0;

循环主体:

for (int i = 1; i <= n; i++)
{
    rowSum = i;

    for (int k = 1; k <= i; k++)
    {
        cout << k << "行和:" << rowSum << endl;
        totalSum += k;
    }
}

外层和内层循环的条件都是正确的,但 rowSum 的赋值与输出语句的位置存在问题。

2. 修正后的完整代码

#include <iostream>
using namespace std;

int main()
{
    int rowSum = 0;
    int totalSum = 0;
    int n;

    cin >> n;

    for (int i = 1; i <= n; i++)
    {
        rowSum = 0;

        for (int k = 1; k <= i; k++)
        {
            cout << k;

            rowSum += k;
            totalSum += k;
        }

        cout << "  行和:" << rowSum << endl;
    }

    cout << "总和:" << totalSum << endl;

    return 0;
}

3. 遇到的问题

问题一:混淆了赋值和累加

最初写的是:

rowSum = i;

这表示直接将当前行数赋值给 rowSum,而不是计算该行数字之和。

例如第三行输出 123,实际需要计算:

rowSum = 0 + 1 + 2 + 3 = 6

因此应该使用:

rowSum += k;

问题二:没有正确处理每行的初始化

rowSum 和 totalSum 的作用不同。

rowSum = 0;

应该放在外层循环内部、内层循环之前。

每开始新的一行,都重新计算这一行的总和。

但:

int totalSum = 0;

只需要在整个循环开始之前初始化一次。

因为 totalSum 要保存所有行的累计结果。

问题三:输出语句放错了位置

最初写的是:

cout << k << "行和:" << rowSum << endl;

并将其放在内层循环中。

这意味着每输出一个数字都会立即换行。

正确做法是先在内层循环中输出数字:

cout << k;

等内层循环全部结束,再输出当前行的总和:

cout << "  行和:" << rowSum << endl;

4. 理解与收获

这道题让我进一步理解了两个累加器的区别:

  • rowSum:保存当前行的总和,每行重新清零。
  • totalSum:保存所有行的总和,始终累加。

同时,也需要特别注意循环体花括号的位置。

同样一条语句,放在内层循环中和放在内层循环结束后,执行次数可能完全不同。


二、数组平均值与条件统计

第二道练习主要巩固两个独立循环的执行顺序。

已知数组:

int arr[6] = {5, 9, 3, 11, 7, 13};

要求计算数组总和、平均值,并找出所有大于平均值的元素及其数量。

1. 修正后的完整代码

#include <iostream>
#include <iterator>
using namespace std;

int main()
{
    int arr[6] = {5, 9, 3, 11, 7, 13};

    int sum = 0;
    double average = 0;
    int count = 0;
    int find;

    int* p = arr;

    // 第一次循环:计算总和
    for (int i = 0; i < 6; i++)
    {
        sum += *p;
        p++;
    }

    // 计算平均值
    average = static_cast<double>(sum) / std::size(arr);

    // 将指针重新指向数组开头
    p = arr;

    cout << "总和:" << sum << endl;
    cout << "平均数:" << average << endl;

    // 第二次循环:筛选元素并计数
    for (int i = 0; i < 6; i++)
    {
        find = *p;

        if (find > average)
        {
            cout << "大于平均值的元素:" << find << endl;
            count++;
        }

        p++;
    }

    cout << "元素个数:" << count << endl;

    return 0;
}

输出:

总和:48
平均数:8
大于平均值的元素:9
大于平均值的元素:11
大于平均值的元素:13
元素个数:3

2. 遇到的问题

问题一:计数器未初始化

最初写的是:

int maxvalue, find;

之后直接执行:

maxvalue++;

问题在于 maxvalue 没有初始值。

对于这里的局部变量,读取未初始化的 int 会产生不可靠的程序行为。

正确写法:

int count = 0;

因为程序需要从零开始统计满足条件的元素数量。

另外,原来的 maxvalue 名称容易让人误认为它用于保存最大值,修改为 count 更符合其实际用途。

问题二:平均值计算中存在整数除法问题

最初写的是:

double average = 0;

average = sum / std::size(arr);

虽然 average 是 double,但除法两侧均为整数类型,计算时会先执行整数除法。

例如:

int sum = 10;
double average = sum / 3;

得到的是 3.0,而不是 3.333...。

正确做法:

average = static_cast<double>(sum) / std::size(arr);

这样可以在除法之前把一个操作数转换为浮点数。

3. 本次已经正确掌握的内容

相比昨天相似的题目,这次有几处明显改进。

第一:两个循环不再错误嵌套。

for (int i = 0; i < 6; i++)
{
    sum += *p;
    p++;
}

average = static_cast<double>(sum) / std::size(arr);

p = arr;

for (int i = 0; i < 6; i++)
{
    // 比较与计数
}

这次已经能正确安排:

第一次循环
    ↓
计算平均值
    ↓
重置指针
    ↓
第二次循环

第二:正确重置了指针。

p = arr;

由于第一次遍历中使用了 p++,指针已经移动到了数组末尾之后的位置。

第二次遍历前,必须让它重新指向数组首元素。

第三:条件判断正确。

if (find > average)

这次已经能够正确地比较当前数组元素与平均值,不再混淆计数器与被比较的值。

4. 理解与收获

这道题进一步强化了:

  • 两个独立循环的执行顺序。
  • p++ 对指针位置的影响。
  • 指针重置。
  • 计数器初始化。
  • 整数除法与浮点除法的区别。

与之前相比,程序整体结构已经更加合理,主要问题集中在变量初始化和数据类型转换方面。


三、正数、负数的分类统计

第三道题要求在一次循环中完成多个统计任务。

给定数组:

int arr[8] = {-3, 5, 0, -7, 12, 8, -2, 4};

需要计算:

  1. 正数个数。
  2. 负数个数。
  3. 正数总和。
  4. 负数总和。

其中,0 既不是正数,也不是负数。

1. 最初代码中的问题

这道题一开始已经正确初始化了所有统计变量:

int Positive = 0;
int Negative = 0;
int Psum = 0;
int Nsum = 0;

随后使用:

value = *p;

读取当前数组元素。

正数判断部分:

if (value > 0)
{
    Psum += value;
    Positive++;
}

这一部分完全正确。

但是负数判断写成了:

else(value < 0);
{
    Nsum += value;
    Negative++;
}

这里出现了 else 语法使用错误。

2. 修正后的完整代码

#include <iostream>
using namespace std;

int main()
{
    int arr[8] = {-3, 5, 0, -7, 12, 8, -2, 4};
    int* p = arr;

    int Positive = 0;
    int Negative = 0;
    int Psum = 0;
    int Nsum = 0;
    int value;

    for (int i = 0; i < 8; i++)
    {
        value = *p;

        if (value > 0)
        {
            Psum += value;
            Positive++;
        }
        else if (value < 0)
        {
            Nsum += value;
            Negative++;
        }

        p++;
    }

    cout << "正数个数:" << Positive << endl;
    cout << "负数个数:" << Negative << endl;
    cout << "正数总和:" << Psum << endl;
    cout << "负数总和:" << Nsum << endl;

    return 0;
}

输出:

正数个数:4
负数个数:3
正数总和:29
负数总和:-12

3. 遇到的问题

问题一:混淆 else 和 else if

最初写的是:

else(value < 0);

但 else 后面不能直接附加判断条件。

如果需要判断第二种情况,应使用:

else if (value < 0)

完整结构:

if (条件1)
{
    // 条件1成立时执行
}
else if (条件2)
{
    // 条件1不成立且条件2成立时执行
}
else
{
    // 前面的条件都不成立时执行
}

本题中不需要最后的 else,因为数组元素为 0 时,不应该更新任何正负数统计变量。

问题二:多余的分号可能破坏条件判断

原来的:

else(value < 0);
{
    Nsum += value;
    Negative++;
}

可以被 C++ 解释为 else 执行一个表达式语句,而后面的 {} 成为独立代码块。

结果负数统计代码不再受 else 控制,每次循环都会执行。

对于当前数组,如果保留原写法,负数计数会变成 8,负数总和会变成 17,而不是正确的 3 和 -12。

因此需要特别注意条件判断语句中的分号和花括号。

4. 本题的进步

相比前两道题,这次所有计数器和累加器都正确初始化为 0。

另外,已经能够在一个循环中同时完成四项统计:

Psum += value;
Positive++;

Nsum += value;
Negative++;

其中:

  • Psum:累加正数的值。
  • Positive:统计正数出现的次数。
  • Nsum:累加负数的值。
  • Negative:统计负数出现的次数。

这说明对累加器和计数器的区别已经有了更明确的认识。


四、今日知识点总结

知识点核心作用for嵌套循环外层控制整体重复,内层执行具体操作rowSum = 0每行重新初始化当前行的总和totalSum += k保留并更新全部行的累计值count++统计满足条件的元素个数sum += *p将指针指向的元素累加p++将指针移动到下一个元素p = arr重置指针,使其指向数组首元素if / else if按条件执行不同的代码分支static_cast<double>()显式转换数据类型std::size(arr)获取固定数组的元素数量

五、三次 Coding 训练进度对比

Day 1|11 月 8 日

主要内容:

  • 偶数统计与累加。
  • 嵌套循环数字三角形。
  • 指针遍历数组。
  • 使用指针寻找最大值。

主要问题:

  • 不熟悉累加表达式。
  • 嵌套循环难以独立编写。
  • 多次出现 i <= 5 的越界错误。
  • 刚开始理解 *(p + i) 与 p++。

Day 2|11 月 10 日

主要内容:

  • 指针寻找最小值及下标。
  • 双指针反转数组。
  • 数组平均值与条件统计。

进步:

  • 循环边界明显改善。
  • 独立完成双指针数组反转。
  • 掌握 while (left < right) 的基本应用。
  • 能够使用多个变量进行统计。

主要问题:

  • 变量初始化容易遗漏。
  • 多个独立循环容易错误地嵌套。
  • 平均值计算和变量更新顺序不够清晰。

Day 3|11 月 11 日

主要内容:

  • 嵌套循环与双累加器。
  • 两次数组遍历与平均值统计。
  • 一次遍历完成正负数分类统计。

进步:

  • 能够独立编写正确的嵌套循环结构。
  • 能够正确使用两个顺序执行的循环。
  • 能够主动重置指针。
  • 能够在一个循环中完成多个统计任务。
  • 第三题的计数器和累加器全部正确初始化。

仍然存在的问题:

  • 累加器清零的位置。
  • 输出语句所属的循环层级。
  • 少数变量初始化遗漏。
  • else if 等条件判断语法。
  • 整数除法与浮点除法的区别。

六、下一阶段学习计划

下一次计划进行 Day 4:循环与指针综合测验。

不再提前提供完整代码框架,主要检查能否独立实现以下内容:

  1. 使用嵌套循环完成输出与累加。
  2. 使用数组和指针完成多条件统计。
  3. 正确初始化并更新多个变量。
  4. 独立确定循环边界和执行顺序。
  5. 避免指针越界和错误的数据类型转换。

测验重点不是掌握多少新的语法,而是能否在没有提示的情况下,把已经学过的知识组织成一个完整程序。

通过基础综合测试后,下一阶段将进入数据结构中的单链表,进一步学习节点、指针连接、遍历和插入操作。

七、今日总结

今天的三道题中,第一题的主要问题是行累加器与输出位置;第二题的程序结构已经基本正确,仅存在计数器初始化和整数除法问题;第三题的核心算法正确,只出现了 else if 语法错误。

相比第一次训练,已经不再频繁出现数组遍历边界错误,也能够更熟练地使用指针进行数组操作。

目前最大的不足已经从单个语法概念转向了多步骤算法的组织、变量状态管理以及代码细节检查。

接下来的训练应继续坚持独立编写,并重点检查每个变量的初始化、每一层循环的职责以及条件判断是否正确。

学习原则:原理理解 → 独立 Coding → 调试纠错 → 总结记录 → 综合应用。

评论