C++零基础到工程实战(4.2):while循环流程控制与条件表达式实战——使用system和cin实现支持ls的Shell
目录
一、本节学习内容概要图
二、前言
三、while 循环的基本逻辑与执行流程
3.1 while 的基本语法
3.2 while 和 for 的区别
四、while 中的 break、continue 与表达式条件
4.1 break:立即结束整个循环
4.2 continue:跳过本次,进入下一次循环
4.3 while 无限循环时为什么一定要有退出逻辑
(1)单线程:
(2)多线程程序:
4.4 while 也可以直接写表达式条件
(1)index++ < 10
(2)++index < 10
五、system 函数的作用与使用方式
5.1 system 是什么
5.2 system("dir") 的作用
六、string 为什么不能直接传给 system
6.1 system 需要的是 const char*
6.2 string 对象可以通过 c_str() 转换
(1)string 是类型
(2)cmd 是对象,也是变量
(3)c_str() 是 string 的成员函数
6.3 这一句怎么理解
七、cin 输入机制与缓冲区理解
7.1 cin >> str 的特点
7.2 为什么包含空格的内容要多次 cin 或者用 getline
(1)多次用 cin >> 读取
(2)使用 getline
7.3 cin.rdbuf()->in_avail() 是什么
八、项目实战:实现一个支持 ls 的简易 Shell
8.1 项目需求
8.2 完整代码
8.3 代码分析
(1) 退出标志位
(2) 持续显示提示符并读取命令
(3) 如果命令是 ls,就转换为 dir
(4) 判断后面是否还带参数
1 )情况一:用户输入 ls
2) 情况二:用户输入 ls c:
(5) 为什么执行时必须写 c_str()
(6) 输入 exit 或 quit 退出程序
(7) 不支持的命令要报错提示
(8) 这个版本有哪些局限
1)目前只支持一个参数
2)不适合读取带空格的路径
3)依赖 Windows 的 dir
九、本节小结
9.1 while 的核心思想
9.2 break 和 continue 的区别
9.3 表达式条件要分清前置和后置自增
9.4 system 是执行系统命令的函数
9.5 string 和 const char* 不是一回事
9.6 cin >> 是按空白符分割输入
9.7 这个 Shell 项目本质上是在练什么
一、本节学习内容概要图
二、前言
在前面的 C++ 学习中,我们已经接触了if、switch、for等基础流程控制语句。到了这一节,重点要进入另一个非常常用的循环结构:while。
while的特点非常明显:
它只有“条件判断”这一个核心入口,只要条件为真,就会一直重复执行循环体中的代码;当条件变为假时,循环结束。
相比for循环,while更适合下面这类场景:
- 不确定循环次数
- 需要一直等待用户输入
- 需要根据运行过程中的状态决定什么时候退出
- 适合写交互式程序、菜单程序、命令行程序、Shell 程序
本节除了讲清楚while的流程控制思想,还会结合system、cin、string::c_str()等知识点,完成一个小项目:在 Windows 上实现一个支持ls命令的简易 Shell
三、while 循环的基本逻辑与执行流程
3.1 while 的基本语法
while的语法非常简单:
while (条件) { 循环体; }它的执行过程可以理解为:
- 先判断条件是否为
true - 如果为
true,执行一次循环体 - 执行完后,再次判断条件
- 条件仍然为
true,继续执行 - 直到条件变为
false,循环结束
例如:
int index = 0; while (index < 5) { cout << index << endl; index++; }执行过程是:
index = 0,满足index < 5,输出0index = 1,满足条件,输出1index = 2,满足条件,输出2index = 3,满足条件,输出3index = 4,满足条件,输出4index = 5,不满足条件,结束循环
3.2 while 和 for 的区别
很多初学者会问:while和for有什么区别?
可以这样理解:
for更适合“循环次数明确”的情况,
- 比如循环 10 次、遍历数组、遍历容器。
而while更适合“循环次数不明确”的情况,比如:
- 用户不退出就一直运行
- 网络连接未断开就一直监听
- 游戏主循环持续运行
- 菜单程序反复等待用户输入
例如:
bool is_exit = false; while (!is_exit) { // 不退出就一直执行 }所以这节用while来写简易 Shell,是非常典型的工程思路。
四、while 中的 break、continue 与表达式条件
4.1 break:立即结束整个循环
break的作用是:直接退出当前循环。
例如:
int index = 0; while (true) { index++; if (index > 5) break; cout << index << " "; }输出结果:
1 2 3 4 5因为当index > 5时,执行break,整个while直接结束。
4.2 continue:跳过本次,进入下一次循环
continue的作用是:结束本次循环剩余语句,直接进入下一轮判断。
例如:
int index = 0; while (index < 10) { index++; if (index % 2 == 0) continue; cout << index << " "; }输出结果:
1 3 5 7 9因为偶数时执行continue,本次后面的cout不执行,直接进入下一次循环。
4.3 while 无限循环时为什么一定要有退出逻辑
最经典的无限循环写法:
while (1) { }或者:
while (true) { }这类写法本身没有问题,很多服务程序、监听程序、菜单程序都会这么写。
但必须注意一点:一定要设计退出条件,通常通过break或标志位结束循环。
例如:
bool is_exit = false; while (!is_exit) { if (某个条件满足) is_exit = true; }否则程序就会一直跑下去,无法正常退出。
(1)单线程:
无限循环如果没有退出逻辑,程序将一直停留在循环中,无法正常结束。
(2)多线程程序:
避免程序退出时子线程还在跑
4.4 while 也可以直接写表达式条件
while的条件里不一定非要写简单变量比较,也可以直接写表达式。
例如:
int index = 0; while (index++ < 10) { cout << index << " "; }这个例子非常适合练习前置、后置自增的理解。
(1)index++ < 10
这是后置自增,先参与比较,再自增。
假设开始时index = 0:
- 比较时用的是旧值
0 - 比较完后
index变成1 - 进入循环后输出的是
1
所以最终输出:
1 2 3 4 5 6 7 8 9 10(2)++index < 10
这是前置自增,先自增,再比较。
int index = 0; while (++index < 10) { cout << index << " "; }执行时:
- 先变成
1,再比较1 < 10
最终输出:
1 2 3 4 5 6 7 8 9这两个写法很像,但结果不同,这正是表达式条件容易出错的地方。
五、system 函数的作用与使用方式
5.1 system 是什么
system是 C/C++ 标准库中提供的一个函数,用来执行操作系统命令。
它的函数原型通常是:
int system(const char* command);使用它时,需要包含头文件:
#include <cstdlib>它的意思可以理解成:
把一条字符串形式的命令,交给操作系统的命令解释器去执行。
也就是说:
system("dir");和你在 Windows 命令行窗口手动输入:
dir效果本质上是一样的。
5.2 system("dir") 的作用
在 Windows 系统中:
system("dir");就相当于:
- 当前程序所在目录打开一个命令执行环境
- 执行
dir- 把当前目录内容打印出来
表示列出当前目录下的文件和子目录。
这就是为什么在这个项目里,我们可以把用户输入的ls转换成 Windows 的dir命令,从而“模拟” Linux 风格的ls。
六、string 为什么不能直接传给 system
6.1 system 需要的是 const char*
system的参数类型是:
const char* command这说明它要接收的是C 风格字符串,而不是 C++ 的std::string对象。
例如下面这样写是不行的:
string cmd = "dir"; system(cmd); // 错误因为cmd是一个string对象,而system想要的是const char*。
6.2 string 对象可以通过 c_str() 转换
std::string提供了一个很常用的成员函数:
c_str()作用是:返回该字符串内部对应的C 风格字符串指针。
写法如下:
string cmd = "dir"; system(cmd.c_str());这里要理解几个点:
(1)string是类型
string cmd;这里的string是类型名。
(2)cmd是对象,也是变量
cmd本质上是一个string类型对象,我们平时也把它叫变量。
(3)c_str()是 string 的成员函数
也就是:cmd这个对象可以调用自己的方法:
cmd.c_str()它返回的是:
const char*于是就可以传给system。
6.3 这一句怎么理解
system(execcmd.c_str());这句话可以拆成两步看:
第一步,先把execcmd从string转成const char*
execcmd.c_str()第二步,把得到的 C 风格字符串交给system
system(...)所以整句的意思就是:
执行变量execcmd里面保存的系统命令。
七、cin 输入机制与缓冲区理解
7.1 cin >> str 的特点
cin >> str;这是最常见的输入写法,它会读取一个“以空白符为分隔”的字符串。
所谓空白符,包括:
- 空格
- Tab
- 回车换行
例如输入:
hello world如果执行:
string str; cin >> str;那么str只会读到:
hello后面的world还留在输入缓冲区中。
7.2 为什么包含空格的内容要多次 cin 或者用 getline
如果你想读取一整行,包括中间空格,那么单纯使用:
cin >> str;是不够的。
有两种常见思路:
(1)多次用cin >>读取
(2)使用getline
如果想一次把整行都读进来,更常见的方法是:
string line; getline(cin, line);这样,整行都可以读到。
所以严格来说:
cin >>适合按“单词”读getline适合按“整行”读
7.3 cin.rdbuf()->in_avail() 是什么
这一句:
cin.rdbuf()->in_avail()表示查看当前输入缓冲区中,还有多少字符可以读取。
在你的代码里,作用是:
if (std::cin.rdbuf()->in_avail() > 1) { std::cin >> arg; execcmd += " " + arg; }目的是判断:ls后面是不是还跟了参数。
如果用户输入:
ls那么通常读完cmd后,缓冲区里可能只剩一个换行符 \n。
如果用户输入:
ls c:那么读完cmd后,缓冲区里通常还会有:
- 空格
c:- 一个换行
所以可读字符数往往会大于 1。
八、项目实战:实现一个支持 ls 的简易 Shell
在 Windows 下实现一个简易命令行程序,让用户输入ls时,底层调用dir命令。
8.1 项目需求
本项目实现以下功能:
- 使用
while持续接收用户输入 - 支持输入
ls - 支持
ls不带参数 - 支持
ls带一个参数 - 使用
system调用 Windows 的dir命令 - 输入
exit或quit时退出程序 - 不支持的命令给出提示
8.2 完整代码
#include <iostream> #include <string> using namespace std; int main() { bool is_exit{ false }; while (!is_exit) { string cmd; string execcmd; std::cout << "xshell>>"; std::cin >> cmd; if (cmd == "ls") { execcmd = "dir"; string arg; if (std::cin.rdbuf()->in_avail() > 1) { std::cin >> arg; execcmd += " " + arg; } system(execcmd.c_str()); } else if (cmd == "exit" || cmd == "quit") { is_exit = true; continue; } else { cerr << "命令" << cmd << "不支持!" << endl; } } return 0; }8.3 代码分析
(1) 退出标志位
bool is_exit{ false };这里定义了一个布尔变量,初始值为false,表示程序暂时不退出。
然后配合:
while (!is_exit)意思就是:
只要 is_exit 还是 false,循环就一直继续。
这就是典型的“标志位控制循环”。
(2) 持续显示提示符并读取命令
std::cout << "xshell>>"; std::cin >> cmd;效果类似很多命令行程序中的输入提示符,比如:
xshell>>用户在这个提示符后输入命令
(3) 如果命令是 ls,就转换为 dir
if (cmd == "ls") { execcmd = "dir"; }因为在Windows 中没有 Linux 那样的ls命令,所以这里手动把ls翻译成dir。
这就是“模拟 Shell 命令映射”的思想。
(4) 判断后面是否还带参数
string arg; if (std::cin.rdbuf()->in_avail() > 1) { std::cin >> arg; execcmd += " " + arg; }这里的目的是支持:
ls c:执行过程如下:
1 )情况一:用户输入ls
cmd读到"ls"- 缓冲区可能只剩换行
- 不读取参数
- 执行:
system("dir");2) 情况二:用户输入ls c:
cmd读到"ls"- 缓冲区里还有
c:\n - 再读一个参数
arg - 拼接成:
"dir c:"最终执行:
system("dir c:");(5) 为什么执行时必须写 c_str()
system(execcmd.c_str());因为:
execcmd是stringsystem要的是const char*
所以要调用:
c_str()完成类型转换。
(6) 输入 exit 或 quit 退出程序
else if (cmd == "exit" || cmd == "quit") { is_exit = true; continue; }这里表示:
- 如果用户输入
exit - 或者输入
quit - 就把退出标志位置为
true
下一轮再判断:
while (!is_exit)就会变成:
while (!true)也就是false,循环结束。
(7) 不支持的命令要报错提示
else { cerr << "命令" << cmd << "不支持!" << endl; }如果用户输入:
abc程序会提示:
命令abc不支持!这也是交互式程序中很重要的一点:即使命令不支持,也要给用户明确反馈。
(8) 这个版本有哪些局限
这个简易 Shell 已经能完成基本功能,但它还有一些限制:
1)目前只支持一个参数
例如:
ls c:可以处理,但像:
ls c: /a当前代码就不够用了。
2)不适合读取带空格的路径
例如:
ls C:\Program Files由于cin >> arg遇到空格就停,所以无法完整读到。
更完整的写法通常要改成getline。
3)依赖 Windows 的dir
也就是说这个程序本质上是:
- 用户输入 Linux 风格的
ls - 程序内部翻译成 Windows 的
dir
它不是跨平台 Shell,只是一个教学演示版。
九、本节小结
这一节表面上是在学while,实际上已经把多个知识点串起来了。
我们需要真正掌握的是:
9.1 while 的核心思想
while只有一个条件判断,只要条件为真,就持续重复执行;条件为假,循环结束。
它特别适合:
- 菜单程序
- 持续输入程序
- 服务循环
- Shell 交互程序
9.2 break 和 continue 的区别
break:退出整个循环continue:跳过本次剩余语句,进入下一轮循环
9.3 表达式条件要分清前置和后置自增
while (index++ < 10) while (++index < 10)这两种写法结果不同,原因在于“先比较还是先自增”。
9.4 system 是执行系统命令的函数
system("dir");本质上就是让程序去调用操作系统命令解释器执行命令。
9.5 string 和 const char* 不是一回事
system不能直接接收string,需要通过:
cmd.c_str() // string的对象.c_str()转成const char*。
9.6 cin >> 是按空白符分割输入
它适合读取单词,不适合直接读取带空格的一整行内容。要读取整行,更推荐getline。
9.7 这个 Shell 项目本质上是在练什么
表面是“支持 ls 命令”,本质是在练:
- while 循环控制
- 用户输入处理
- 条件判断
- 字符串拼接
- 系统命令调用
- 退出逻辑设计
