当前位置: 首页 > news >正文

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++ 学习中,我们已经接触了ifswitchfor等基础流程控制语句。到了这一节,重点要进入另一个非常常用的循环结构:while

while的特点非常明显:

只有“条件判断”这一个核心入口,只要条件为真,就会一直重复执行循环体中的代码;当条件变为假时,循环结束。

相比for循环,while更适合下面这类场景:

  1. 不确定循环次数
  2. 需要一直等待用户输入
  3. 需要根据运行过程中的状态决定什么时候退出
  4. 适合写交互式程序、菜单程序、命令行程序、Shell 程序

本节除了讲清楚while的流程控制思想,还会结合systemcinstring::c_str()等知识点,完成一个小项目:在 Windows 上实现一个支持ls命令的简易 Shell


三、while 循环的基本逻辑与执行流程

3.1 while 的基本语法

while的语法非常简单:

while (条件) { 循环体; }

它的执行过程可以理解为:

  1. 先判断条件是否为true
  2. 如果为true,执行一次循环体
  3. 执行完后,再次判断条件
  4. 条件仍然为true,继续执行
  5. 直到条件变为false,循环结束

例如:

int index = 0; while (index < 5) { cout << index << endl; index++; }

执行过程是:

  • index = 0,满足index < 5,输出0
  • index = 1,满足条件,输出1
  • index = 2,满足条件,输出2
  • index = 3,满足条件,输出3
  • index = 4,满足条件,输出4
  • index = 5,不满足条件,结束循环

3.2 while 和 for 的区别

很多初学者会问:whilefor有什么区别?

可以这样理解:

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());

这句话可以拆成两步看:

第一步,先把execcmdstring转成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 项目需求

本项目实现以下功能:

  1. 使用while持续接收用户输入
  2. 支持输入ls
  3. 支持ls不带参数
  4. 支持ls带一个参数
  5. 使用system调用 Windows 的dir命令
  6. 输入exitquit时退出程序
  7. 不支持的命令给出提示

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());

因为:

  • execcmdstring
  • system要的是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 循环控制
  • 用户输入处理
  • 条件判断
  • 字符串拼接
  • 系统命令调用
  • 退出逻辑设计
http://www.cnnetsun.cn/news/1878606.html

相关文章:

  • PyTorch自定义损失超简单
  • 2026年嘎嘎降AI支持哪些检测平台?9大平台实测验证结果
  • DAMO-YOLO TinyNAS保姆级教学:EagleEye日志分析、错误排查与常见报错解决方案
  • gma中计算CWDI(作物水分亏缺指数)的源代码
  • 知网AI率高想降下来,嘎嘎降AI、比话降AI、率零横评
  • 零基础玩转Sambert语音合成:开箱即用镜像,小白也能做专业配音
  • GLDAS数据变量单位速查与避坑指南:别再搞混土壤湿度和蒸散发单位了!
  • 简单理解:Qi 无线充电
  • 2026年抖音买单真相:3公里内精准引流背后的4大红利
  • 每天睡前问三个问题,比检查作业更有效
  • 安科瑞AIM-T系列工业IT绝缘监测及故障定位解决方案为关键供电场所筑牢安全防线
  • 1 【3D Gaussian Splatting: From Theory to Real-Time Implementation】第一级:基础理论与数学建模
  • 2026届最火的降重复率方案推荐榜单
  • 【2026年最新600套毕设项目分享】微信小程序电影订票系统(30048)
  • 大模型学习指南:收藏这份资料,小白程序员轻松掌握RAG,开启AI新技能!
  • 后端转AI大模型应用开发:小白必看收藏!2026年真实路径与避坑指南
  • OneAPI部署实操手册:从零配置到多渠道管理,支持腾讯混元、通义千问、文心一言等全生态
  • Sub-VLAN 跨三层通信核心知识点(精简版)
  • 32TOPS算力+工业级宽温适配!SE110S-WA32边缘计算微服务器全解析
  • 32 openclaw容器化部署:Docker与Kubernetes集成指南
  • 基于模型剪枝与量化的YOLOv5边缘计算加速:从训练到部署完整实战
  • Jupyter Notebook白屏问题排查与解决全记录
  • arm64麒麟服务器内网离线安装minio
  • 从链表到二叉树:树形结构的入门与核心性质解析
  • 麒麟V10生产环境Nginx 1.28.0部署全攻略:从源码编译到极致优化
  • ConvNeXt 系列改进:ConvNeXt 添加 MetaFormer 风格池化层,简化 Block 并保持性能
  • 该技术通过智能算法识别论文重复内容,并借助语义改写与篇章重构提升文本独特性
  • 【我的Android进阶之旅】解决Android Studio 运行gradle命令时报错: 错误: 编码GBK的不可映射字符
  • Hi3519DV500_Uboot环境变量的定制化配置与实战烧录指南
  • ESP32/ESP8622 -- 使用MQTT协议连接云平台(带图文说明)