🚀重生之我在10天内卷赢C++ - DAY 2
导师寄语:Yo, C++新星!昨天,我们创造了一个只会“一条路走到黑”的程序,就像一个只会按剧本念台词的机器人。今天,我们要给这个机器人装上“大脑”和“发动机”!我们将教它如何看情况办事(条件语句),如何不知疲倦地干活(循环语句),以及如何整理一堆东西(数组)。学完今天的内容,你的程序就不再是“傻白甜”,而是一个会思考、有逻辑的“小机灵”了!
🎯 今日目标
- 掌握
if-else
和switch
,让程序学会在十字路口做选择。 - 精通
for
、while
循环,把重复的苦力活交给程序。 - 学会使用数组,一次性管理多个同类型的数据。
- 深入了解
std::string
,玩转字符串。 - 学习如何动态开辟空间以及释放和处理
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 (变量) {
case 值1:
// 如果变量等于值1,执行这里的代码
break; // 非常重要!像个刹车,防止“冲”到下一个case
case 值2:
// 如果变量等于值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);
工作流程拆解:
条件(condition)
:首先,对?
前面的条件表达式进行求值,结果必须是true
或false
。?
(提问):如果条件为true
…表达式1(expression_if_true)
:…那么整个三元表达式的值就是冒号:
前面的表达式1
的值。:
(否则):如果条件为false
…表达式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-while
和 while
很像,但它保证循环体至少执行一次,因为它是先做事,再判断。
语法结构:
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 动态开辟空间 (new
和 delete
)
在C++中,除了在栈上自动创建变量,我们还可以在一个叫做堆 (Heap) 的巨大内存池中手动申请和释放内存。这给了我们极大的灵活性,可以创建生命周期不受函数作用域限制的对象。
语法格式
申请内存 (new
)
开辟单个变量/对象空间:
数据类型* 指针名 = new 数据类型(可选的初始化参数);
开辟数组空间:
数据类型* 指针名 = new 数据类型[数组大小];
释放内存 (delete
)
释放单个变量/对象空间:
delete 指针名;
释放数组空间:
delete[] 指针名;
将指针置为空,防止成为“野指针”:
指针名 = nullptr;
血的教训:
new
和delete
,new[]
和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-else
和switch
做出判断。 - 用
for
和while
不知疲倦地工作。 - 用数组
[]
来管理一批数据。
你的工具箱越来越丰富了!你已经从一个只能写“剧本”的编剧,变成了一个能指导机器人行动的“导演”!
卷王小贴士:
写循环和数组时,最容易犯的错误是**“差一错误” (Off-by-One Error)**。比如for (int i = 1; i <= 5; i++)
循环5次,但数组索引是0
到4
。如果你在循环里用scores[i]
,当i
是5
时就会越界!要格外小心循环的起止条件和数组的索引范围。
✍️ DAY 2 作业
终极猜数字游戏:
- 在今天
while
循环的猜数字游戏基础上进行升级。 - 程序随机生成一个1到100之间的数字作为秘密数字(提示:你需要
#include <cstdlib>
和#include <ctime>
,然后使用srand(time(0)); int secret_num = rand() % 100 + 1;
来生成随机数)。 - 玩家有10次猜测机会。
- 每次猜测后,如果没猜对,程序提示“太高了”或“太低了”,并告诉玩家还剩几次机会。
- 如果10次机会用完还没猜对,游戏结束,告诉玩家秘密数字是多少。
- 如果猜对了,提前结束游戏,并显示祝贺信息。
- 在今天
班级最高分查找器:
- 编写一个程序
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]
)都上来和“擂主”比一下。 - 如果挑战者比擂主分数高,那么挑战者就成为新的“擂主”。
- 一圈下来,最后还站在台上的,就是分数最高的。这个思想在编程中非常常用。
点个赞和关注,更多知识包你进步,谢谢!!!你的支持就是我更新的最大动力