重生之我在10天内卷赢C++ - DAY 2

发布于:2025-07-31 ⋅ 阅读:(14) ⋅ 点赞:(0)

🚀重生之我在10天内卷赢C++ - DAY 2

导师寄语:Yo, C++新星!昨天,我们创造了一个只会“一条路走到黑”的程序,就像一个只会按剧本念台词的机器人。今天,我们要给这个机器人装上“大脑”和“发动机”!我们将教它如何看情况办事(条件语句),如何不知疲倦地干活(循环语句),以及如何整理一堆东西(数组)。学完今天的内容,你的程序就不再是“傻白甜”,而是一个会思考、有逻辑的“小机灵”了!

🎯 今日目标

  1. 掌握 if-elseswitch,让程序学会在十字路口做选择。
  2. 精通 forwhile 循环,把重复的苦力活交给程序。
  3. 学会使用数组,一次性管理多个同类型的数据。
  4. 深入了解 std::string,玩转字符串。
  5. 学习如何动态开辟空间以及释放和处理

1. 程序的分岔路口:条件语句

现实生活中,我们无时无刻不在做选择:“如果下雨,就带伞;否则,就戴墨镜。” 程序也需要这种能力。

1.1 if-else:最经典的选择题

if 就像一个保安,它会检查一个条件 (condition),如果条件为 真 (true),就放行,执行 {} 里的代码;否则就跳过。

语法结构:

if (条件) {
    // 如果条件为真,就执行这里的代码
} else {
    // 否则(条件为假),就执行这里的代码
}

例子:网吧年龄检测器

创建一个 age_checker.cpp 文件。

#include <iostream>

using namespace std;

int main() {
    int age;

    cout << "欢迎光临!请输入您的年龄: ";
    cin >> age;

    if (age >= 18) {
        // 条件 age >= 18 为真
        cout << "成年人,欢迎上网!祝您游戏愉快!" << endl;
    } else {
        // 条件 age >= 18 为假
        cout << "小朋友,未成年人禁止入内哦!快回家写作业吧!" << endl;
    }

    return 0;
}

编译运行,试试输入不同的年龄,看看保安如何反应!

进阶版:if - else if - else (多重选择)
如果选择不止两种呢?比如根据考试分数评级。

// 伪代码,演示逻辑
int score = 85;

if (score >= 90) {
    cout << "优秀!";
} else if (score >= 80) { // 如果上面if不满足,再检查这个
    cout << "良好!";
} else if (score >= 60) { // 上面两个都不满足,再检查这个
    cout << "及格!";
} else { // 如果以上所有条件都不满足
    cout << "很遗憾,需要补考。";
}

1.2 switch-case:专一的“选择困难症”终结者

当你的判断条件是检查一个变量是否等于某个固定的值时,switch-case 比一长串 if-else if 更清晰。

语法结构:
它就像一个自动贩卖机:你投币(传入变量),它根据你按的按钮(case),掉出对应的饮料(执行代码)。

switch (变量) {
    case1:
        // 如果变量等于值1,执行这里的代码
        break; // 非常重要!像个刹车,防止“冲”到下一个case
    case2:
        // 如果变量等于值2,执行这里的代码
        break;
    default:
        // 如果上面所有case都不匹配,执行这里的代码
        break;
}

例子:每日菜谱推荐
创建 menu.cpp

#include <iostream>

using namespace std;

int main() {
    int day;

    cout << "请输入今天是周几 (1-7): ";
    cin >> day;

    switch (day) {
        case 1:
            cout << "周一,吃【红烧肉】,补充能量!" << endl;
            break;
        case 2:
        case 3:
        case 4:
            cout << "工作日,吃【健康沙拉】,保持清醒!" << endl;
            break; // case 2,3,4会共享这个输出
        case 5:
            cout << "周五,吃【炸鸡啤酒】,放飞自我!" << endl;
            break;
        case 6:
        case 7:
            cout << "周末,吃【火锅】,犒劳自己!" << endl;
            break;
        default:
            cout << "输入错误!地球上一周只有7天!" << endl;
            break;
    }

    return 0;
}

导师划重点break 至关重要!如果没有它,程序会从匹配的 case 开始,一直往下执行,直到遇到一个 break 或者 switch 结束,这叫“穿透”。有时候我们故意利用穿透(如上面的周二、三、四),但大多数时候是Bug的来源!

1.3 三元条件运算符 (? :)

三元运算符是C++中 if-else 语句的一种紧凑、简洁的替代形式,特别适合用在简单的条件赋值语句中。它是C++里唯一一个需要三个操作数的运算符。

语法格式
条件(condition) ? 表达式1(expression_if_true) : 表达式2(expression_if_false);

工作流程拆解:

  1. 条件(condition):首先,对 ? 前面的条件表达式进行求值,结果必须是 truefalse
  2. ? (提问):如果条件为 true
  3. 表达式1(expression_if_true):…那么整个三元表达式的值就是冒号 : 前面的 表达式1 的值。
  4. : (否则):如果条件为 false
  5. 表达式2(expression_if_false):…那么整个三元表达式的值就是冒号 : 后面的 表达式2 的值。

示例与应用场景
示例1:求两个数的最大值 (经典用法)

传统 if-else 写法:

#include <iostream>

int main() {
    int a = 100, b = 42;
    int max_val;
    if (a > b) {
        max_val = a;
    } else {
        max_val = b;
    }
    std::cout << "最大值是: " << max_val << std::endl;
}

三元运算符写法:

#include <iostream>

int main() {
    int a = 100, b = 42;
    // 一行代码搞定!
    int max_val = (a > b) ? a : b; 
    std::cout << "最大值是: " << max_val << std::endl; // 输出: 100
}

幽默解读:这行代码就像在自言自语:“a > b 吗?是的话,max_val 就是 a;不是的话,max_val 就是 b。”

示例2:根据状态码返回字符串

传统 if-else 写法:

#include <iostream>
#include <string>

std::string get_status_string(int code) {
    if (code == 200) {
        return "OK";
    } else {
        return "Error";
    }
}

三元运算符写法:

#include <iostream>
#include <string>

std::string get_status_string(int code) {
    // 直接在 return 语句中使用
    return (code == 200) ? "OK" : "Error";
}

int main() {
    std::cout << get_status_string(200) << std::endl; // 输出: OK
    std::cout << get_status_string(404) << std::endl; // 输出: Error
}

核心要点:三元运算符是一个表达式,它有自己的值,因此可以被用在赋值、return 语句或函数参数等任何需要一个值的地方。


2. 勤劳的小蜜蜂:循环语句

想在屏幕上打印100遍“我爱C++”,你不会真的复制粘贴100遍 cout 吧?不会吧不会吧?循环语句就是来解救你的!

2.1 for 循环:预知终点的马拉松

当你明确知道要重复多少次时,for 循环是你的最佳选择。

语法结构:
for (初始化; 循环条件; 每次循环后的操作)

for (int i = 1; i <= 10; i++) {
    // 这段代码会被执行10次
    // i会从1变到10
}
  • int i = 1;起点。创建一个计数器 i,并让它从1开始。
  • i <= 10;终点。只要 i 小于等于10,循环就继续。
  • i++步进。每次循环结束后,让 i 的值加1。

例子:罚抄10遍

#include <iostream>

using namespace std;

int main() {
    cout << "开始罚抄!" << endl;
    for (int i = 1; i <= 10; i++) {
        cout << "第" << i << "遍: 我再也不在代码里写bug了。" << endl;
    }
    cout << "抄完了,手好酸..." << endl;
    return 0;
}

2.2 while 循环:不到长城非好汉

当你不知道要循环多少次,只知道一个停止的条件时,用 while

语法结构:
while (循环条件)

while (没到长城) {
    // 继续往前走
}

例子:猜数字游戏(简化版)
程序想一个数字,你一直猜,直到猜对为止。

#include <iostream>

using namespace std;

int main() {
    int secret_num = 66;
    int guess = 0; // 初始化一个肯定不相等的值

    cout << "猜猜我心里的数字是多少? (提示: 1-100)" << endl;

    while (guess != secret_num) {
        cout << "请输入你的猜测: ";
        cin >> guess;

        if (guess < secret_num) {
            cout << "太小了,再试试!" << endl;
        } else if (guess > secret_num) {
            cout << "太大了,再试试!" << endl;
        }
    }

    cout << "恭喜你,猜对了!不愧是你!" << endl;
    return 0;
}

2.3 do-while 循环:先斩后奏

do-whilewhile 很像,但它保证循环体至少执行一次,因为它是先做事,再判断。

语法结构:
do { ... } while (循环条件);

例子:点餐系统
不管用户想不想点,至少先把菜单显示一次。

#include <iostream>

using namespace std;

int main() {
    char choice;

    do {
        cout << "\n------ 卷王餐厅 ------" << endl;
        cout << "a. 宫保鸡丁 (28元)" << endl;
        cout << "b. 鱼香肉丝 (25元)" << endl;
        cout << "q. 不吃了,退出" << endl;
        cout << "----------------------" << endl;
        cout << "请输入您的选择: ";
        cin >> choice;
        
        // 此处可以加入switch处理具体点餐逻辑
        
    } while (choice != 'q'); // 只要输入的不是q,就一直循环

    cout << "感谢光临,期待您下次光临!" << endl;

    return 0;
}

3. 程序的“储物柜”:数组与字符串

如果想存一个班50个学生的成绩,难道要定义50个变量吗?int score1, score2, ...?太可怕了!数组 (Array) 就是来解决这个问题的。

3.1 C风格数组:一排整齐的“储物柜”

数组就是一连串相同类型的变量,它们在内存里排得整整齐齐。

声明一个数组:
数据类型 数组名[大小];

// 声明一个能装5个整数的储物柜
int scores[5]; 

使用数组:通过索引
数组的每个“柜子”都有一个编号,叫索引 (index)最最最重要的一点:索引是从 0 开始的!

对于 scores[5],它的柜子编号是 0, 1, 2, 3, 4

例子:计算平均分

#include <iostream>

using namespace std;

int main() {
    // 声明并初始化一个包含5个分数的数组
    int scores[5] = {88, 92, 75, 98, 85};
    int sum = 0;

    // 用for循环遍历数组,计算总分
    for (int i = 0; i < 5; i++) {
        cout << "第" << i + 1 << "位同学的分数是: " << scores[i] << endl;
        sum = sum + scores[i]; // 累加
    }

    double average = sum / 5.0; // 除以5.0确保结果是小数

    cout << "--------------------------" << endl;
    cout << "总分是: " << sum << endl;
    cout << "平均分是: " << average << endl;

    return 0;
}

导师警告:C风格数组有个致命弱点:大小是固定的,而且它自己不知道自己有多大(上面的 5 是我们自己记住的)。这很危险,容易造成数组越界(比如你试图访问 scores[5],但最大索引是4),这是C++中最常见的Bug之一!后面我们会学更强大的 std::vector

3.2 std::string 的进阶玩法

我们在第一天已经用过 std::string 了。现在我们知道,它其实就是一个管理 char 类型字符的“智能数组”。

std::string 的一些超能力:

  • .length() 或 .size():获取字符串长度。
  • + 连接字符串:比C语言方便一万倍。
  • [] 访问单个字符:和数组一样,索引从0开始。
#include <iostream>
#include <string>

using namespace std;

int main() {
    string first_name = "Li";
    string last_name = "Hua";

    // 拼接字符串
    string full_name = first_name + " " + last_name;
    cout << "全名: " << full_name << endl;

    // 获取长度
    cout << "名字长度: " << full_name.length() << "个字符" << endl;

    // 访问单个字符
    cout << "姓氏的首字母是: " << full_name[0] << endl;

    // 修改单个字符
    full_name[0] = 'W';
    cout << "改姓后: " << full_name << endl;

    return 0;
}

3.3 动态开辟空间 (newdelete)

在C++中,除了在栈上自动创建变量,我们还可以在一个叫做堆 (Heap) 的巨大内存池中手动申请和释放内存。这给了我们极大的灵活性,可以创建生命周期不受函数作用域限制的对象。

语法格式
申请内存 (new)
  • 开辟单个变量/对象空间:

    数据类型* 指针名 = new 数据类型(可选的初始化参数);
    
  • 开辟数组空间:

    数据类型* 指针名 = new 数据类型[数组大小];
    
释放内存 (delete)
  • 释放单个变量/对象空间:

    delete 指针名;
    
  • 释放数组空间:

    delete[] 指针名;
    
  • 将指针置为空,防止成为“野指针”:

    指针名 = nullptr;
    

血的教训newdeletenew[]delete[] 必须成对使用!混用会导致未定义的行为(通常是程序崩溃或内存损坏)。


示例与应用场景
示例1:动态创建一个整数
#include <iostream>

int main() {
    // 1. 在堆上申请一个int大小的空间,并用指针ptr指向它
    int* ptr = new int;

    // 2. 通过指针操作这块内存
    *ptr = 1024;
    std::cout << "堆上的值是: " << *ptr << std::endl;

    // 3. 使用完毕,手动释放内存
    delete ptr;

    // 4. 将指针置为空,防止成为“野指针”
    ptr = nullptr;
    
    return 0;
}
示例2:动态创建一个自定义类的对象

假设我们有一个Car类:

class Car {
public:
    Car(std::string brand) { /* ... */ }
    ~Car() { /* ... */ }
    void drive() { /* ... */ }
};

动态创建和使用:

// 1. 在堆上创建Car对象,并调用其构造函数
Car* my_car_ptr = new Car("Tesla");

// 2. 通过指针的箭头运算符 -> 调用成员函数
my_car_ptr->drive();

// 3. 使用完毕,delete会先调用Car的析构函数,再释放内存
delete my_car_ptr;
my_car_ptr = nullptr;
示例3:动态创建一个数组
#include <iostream>

int main() {
    int size = 5;
    // 1. 在堆上创建一个能容纳5个整数的数组
    int* arr_ptr = new int[size];

    // 2. 像普通数组一样使用它
    for (int i = 0; i < size; ++i) {
        arr_ptr[i] = (i + 1) * 10;
        std::cout << arr_ptr[i] << " ";
    }
    std::cout << std::endl;

    // 3. 使用完毕,必须用 delete[] 来释放整个数组
    delete[] arr_ptr;
    arr_ptr = nullptr;

    return 0;
}

核心要点

  • 谁申请,谁释放new返回的指针是管理这块内存的唯一凭证,你必须负责在合适的时机delete它。
  • 内存泄漏new了但忘记delete,这块内存就永远丢失了,直到程序结束。
  • 现代C++建议:由于手动管理new/delete极易出错,现代C++强烈推荐使用智能指针 (std::unique_ptr, std::shared_ptr) 来自动管理动态内存。智能指针是Day 8的核心内容,它能让你享受动态内存的好处,而无需承担忘记delete的风险。

总结与小贴士

今天我们给程序装上了“大脑”,它现在能:

  • if-elseswitch 做出判断。
  • forwhile 不知疲倦地工作。
  • 用数组 [] 来管理一批数据。

你的工具箱越来越丰富了!你已经从一个只能写“剧本”的编剧,变成了一个能指导机器人行动的“导演”!

卷王小贴士
写循环和数组时,最容易犯的错误是**“差一错误” (Off-by-One Error)**。比如 for (int i = 1; i <= 5; i++) 循环5次,但数组索引是 04。如果你在循环里用 scores[i],当 i5 时就会越界!要格外小心循环的起止条件和数组的索引范围。


✍️ DAY 2 作业

  1. 终极猜数字游戏

    • 在今天 while 循环的猜数字游戏基础上进行升级。
    • 程序随机生成一个1到100之间的数字作为秘密数字(提示:你需要 #include <cstdlib>#include <ctime>,然后使用 srand(time(0)); int secret_num = rand() % 100 + 1; 来生成随机数)。
    • 玩家有10次猜测机会。
    • 每次猜测后,如果没猜对,程序提示“太高了”或“太低了”,并告诉玩家还剩几次机会。
    • 如果10次机会用完还没猜对,游戏结束,告诉玩家秘密数字是多少。
    • 如果猜对了,提前结束游戏,并显示祝贺信息。
  2. 班级最高分查找器

    • 编写一个程序 max_score_finder.cpp
    • 定义一个包含10个学生分数的int数组,分数可以自己随便写。
    • 使用 for 循环遍历数组,找出其中的最高分。
    • 最后,打印出这个最高分。

💡 作业答案与解析

1. 终极猜数字游戏

文件:guess_the_number.cpp

#include <iostream>
#include <cstdlib> // 用于 rand() 和 srand()
#include <ctime>   // 用于 time()

using namespace std;

int main() {
    // 1. 生成随机数种子
    srand(time(0)); 

    // 2. 生成 1-100 之间的随机数
    int secret_num = rand() % 100 + 1; 

    int guess;
    int chances = 10;
    bool guessed_correctly = false;

    cout << "--- 终极猜数字游戏 ---" << endl;
    cout << "我已经想好了一个1-100的数字,你有 " << chances << " 次机会!" << endl;

    // 使用 for 循环来控制机会次数
    for (int i = 1; i <= chances; i++) {
        cout << "\n--- 第 " << i << " 次猜测 ---" << endl;
        cout << "请输入你的猜测: ";
        cin >> guess;

        if (guess < secret_num) {
            cout << "太小了!你还剩下 " << chances - i << " 次机会。" << endl;
        } else if (guess > secret_num) {
            cout << "太大了!你还剩下 " << chances - i << " 次机会。" << endl;
        } else {
            cout << "\n🎉 太棒了!你猜对了!秘密数字就是 " << secret_num << "!🎉" << endl;
            guessed_correctly = true;
            break; // 猜对了,直接跳出循环
        }
    }

    // 在循环结束后,检查是否是因为机会用完了还没猜对
    if (!guessed_correctly) {
        cout << "\n游戏结束!很遗憾,你没猜出来。" << endl;
        cout << "秘密数字其实是: " << secret_num << endl;
    }

    return 0;
}

编译与运行:

$ g++ guess_the_number.cpp -o guess_game
$ ./guess_game

关键点解析:

  • srand(time(0));:这是初始化随机数生成器。time(0) 获取当前时间作为“种子”,确保每次运行程序时,rand() 产生的随机数序列都不同。这行代码在程序里只需要执行一次。
  • rand() % 100 + 1;rand() 生成一个很大的随机整数,% 100 的结果是 0-99,再 + 1 就变成了 1-100。
  • 我们用 for 循环来控制机会,这比 while 更直观,因为我们明确知道总共要循环10次。
  • guessed_correctly 这个 bool 变量像个旗帜,用来标记玩家是否猜对了。循环结束后,通过检查这个“旗帜”的状态,我们就能知道游戏结束的原因。

2. 班级最高分查找器

文件:max_score_finder.cpp

#include <iostream>

using namespace std;

int main() {
    int scores[10] = {88, 95, 72, 68, 99, 81, 92, 77, 86, 94};
    
    // 假设第一个分数就是最高分
    int max_score = scores[0]; 

    cout << "班级所有分数如下:" << endl;
    for (int i = 0; i < 10; i++) {
        cout << scores[i] << " ";

        // 核心比较逻辑
        if (scores[i] > max_score) {
            // 如果发现一个比当前max_score还高的分数
            // 就更新max_score
            max_score = scores[i];
        }
    }
    cout << endl; // 换行

    cout << "------------------------" << endl;
    cout << "本次考试的最高分是: " << max_score << endl;

    return 0;
}

编译与运行:

$ g++ max_score_finder.cpp -o find_max
$ ./find_max

输出:

班级所有分数如下:
88 95 72 68 99 81 92 77 86 94 
------------------------
本次考试的最高分是: 99

关键点解析:

  • “打擂台”算法:这个查找最高分的方法很经典。我们先随便请一个人(scores[0])当“擂主”(max_score)。
  • 然后,让数组里的每一个人(从 scores[0]scores[9])都上来和“擂主”比一下。
  • 如果挑战者比擂主分数高,那么挑战者就成为新的“擂主”。
  • 一圈下来,最后还站在台上的,就是分数最高的。这个思想在编程中非常常用。

点个赞和关注,更多知识包你进步,谢谢!!!你的支持就是我更新的最大动力


网站公告

今日签到

点亮在社区的每一天
去签到