IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言进阶——循环语句缺陷分析 -> 正文阅读

[C++知识库]C语言进阶——循环语句缺陷分析

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。


你是否也有过下面的体会?

为什么刚开始学习C语言时很喜欢用for循环语句,但逐渐发现有经验的工程师都在用while和do-while循环(看过内核代码的小伙伴应该也注意到这点了)?

在刚开始写循环语句时总分不清什么时候用for,什么时候用while,什么时候又该使用do-while;刚把这些问题搞清楚了,觉得已经熟练掌握了所有循环语句的使用,但又时不时遇到一个新的问题——总得在循环语句中,甚至是循环语句前和循环语句后添加代码,调整语句顺序,否则最终的逻辑就不正确,甚至是出现指针的解引用错误。

当遇到这些问题时,你是否怀疑过C语言的循环语句是自带缺陷的?下面就让我们一起找出困扰我们编程的幕后黑手吧~~

1 循环语句的三要素

实现一个循环语句的三要素是:

  • A. 判断:判断退出条件是否满足,如果满足则退出循环;如果不满足则循环继续。
  • B. 执行:执行需要循环的内容;内容中一般都会引用循环变量,否则循环就是无差别的。
  • C. 移动:移动循环变量,否则退出条件永远无法满足,循环就演变成了死循环,这也是初学者很容易落的。

根据上述三个要素的执行顺序,可以得到下述6种排列组合。

编号顺序
1A -> B -> C
2A -> C -> B
3B -> C -> A
4C -> B -> A
5B -> A -> C
6C -> A -> B

你所编写的程序中都逃不出以上这六种排列组合,下面我们对其一一进行剖析。

2 使用不同循环语句实现六种排列组合

2.1 第一种排列(ABC)

这种顺序是我们最最熟悉的,也是最常见的。实现起来并不复杂,也是C语言循环语句最擅长处理的情况。

  1. 使用for语句实现
/* 使用for语句实现ABC */
for (; A; C) {
    B;
}
  1. 使用while语句实现
/* 使用while语句实现ABC */
while (A) {
    B;
    C;
}

2.2 第二种排列(ACB)

这种顺序我们就不是经常见到了,所以,for语句对它的支持就不是那么友好了,但是while语句没有受到任何影响。

  1. 使用for语句实现
/* 使用for语句实现ACB */
for (; A; ;) {
    C;
    B;
}
  1. 使用while语句实现
/* 使用while语句实现ACB */
while (A) {
    C;
    B;
}

2.3 第三种排列(BCA)

对于这种排列,for语句彻底没脾气了,while语句也蔫儿了,do-while语句可以大显身手了。

  1. 使用for语句实现
/* 使用for语句实现BCA */
for (B, C; A; C;) { /*可读性变得很差,也有很多重复代码*/
    B;
}
  1. 使用while语句实现
/* 使用while语句实现BCA */
B; /*已经需要在while语句外额外加重复代码了*/
C;
while (A) {
    B;
    C;
}
  1. 使用do-while语句实现
/* 使用do-while语句实现BCA */
do {
    B;
    C;
}while (A); /*do-while就是为此而生啊(看来设计该语句的老前辈已经仔细考虑过该排列了)*/

2.4 第四种排列(CBA)

这种排列跟上一种类似。不过对for语句就更不友好了。

  1. 使用for语句实现
/* 使用for语句实现CBA */
for (C, B; A; C,B;) /*这种形式看似一行就处理结束了,但是真实情况是B的内容会很多*/
  1. 使用while语句实现
/* 使用while语句实现CBA */
C; /*重复代码*/
B;
while (A) {
    C;
    B;
}
  1. 使用do-while语句实现
/* 使用do-while语句实现CBA */
do {
    C;
    B;
}while (A); /*do-while语句默默地说:“这里才是我的战场”。*/

2.5 第五种排列(BAC)

对于这种排列,C语言的循环语句们心里都在想,这是什么鬼地方,没见过啊~~

  1. 使用for语句实现
/* 使用for语句实现BAC */
for (B; A; ;) { /*有重复代码*/
    C;
    B;
}
  1. 使用while语句实现
/* 使用while语句实现BAC */
B; /*有重复代码*/
while(A) {
    C;
    B;
}
  1. 使用do-while语句实现

它说它不在家,让我们去找break。

  1. 使用while-break语句实现
/* 使用while-break语句实现 */
while1{
 B;
 if (!A)
     break;
 C;
}

2.6 第六种排列(CAB)

这种排列和第五种类似,不使用break仍然无法解决代码重复问题。

  1. 使用for语句实现
/* 使用for语句实现CAB */
for (C; A; C) {
    B;
}
  1. 使用while语句实现
/* 使用while语句实现CAB */
C;
while (A) {
    B;
    C;
}
  1. 使用while-break语句实现
/* 使用while-break语句实现CAB */
while (1) {
    C;
    if (!A)
        break;
    B;
}

3 什么时候用for循环语句

通过上面的实验和摸索,我们发现for语句最适合的场景就是第一种排列(ABC)的场景。在应用与其他场景时要么就退化为while,要么就束手无策了。

这就是for语句的缺陷之所在。

所以,你去看Linux内核或者其他大型项目,其中的for语句都是比较少的,你所见到的基本都长成下面这两种样子。

/* for循环的第一种应用范式 */
#define MAX 100
int i;
for (i = 0; i < MAX; ++i) {
    /* do somthing you like. */
}

我是分割线。。。

/* for循环的第二种应用范式 */
for (;;) {
    /* do somthing you like. */
}

第二种应用范式一般都是用在线程死循环中,当然死循环使用while(1)也可以,只能说是大家都这么用习惯了而已,你可以选择自己喜欢的方式。

4 什么时候用while循环语句

while语句适用于第一种排列(ABC)和第二种排列(ACB)场景。

这种场景的特点是——A在最开始执行

5 什么时候用do-while循环语句

do-while语句适用于第三种排列(BCA)和第四种排列(CBA)场景。

这种场景的特点是——A在最后执行

6 其他情况

其他情况包括第五种排列(BAC)和第六种排列(CAB)场景,以及其他更复杂的场景。

这种场景的特点是——A在中间执行

这种情景一般就采用whie-break语句来cover了。其实,该语句可以cover住所有场景。

爱刨根问题的同学可能会问,怎么会有这种场景呢?真的有需求么?

那我在此给出一个CABC真实场景的小例子吧,起到抛砖引玉的作用。

/* 重点关注外层循环;关注pstr指针变量 */
while(1){
    pstr = find_cmd(format_str, pstr); /*C[移动]:这一步会移动pstr指针*/
    if (NULL != pstr) /*A[判断]:在使用前需要先判断;看指针是否合法,非法则退出循环*/
    {   
        /* B[执行]:执行开始(读取字符串内容并打印)*/
        printf("----------------------------------------------------------------------------\n");
        printf("|| name    \t| %s \n",pstr->name);
        printf("|| brief   \t| %s \n",pstr->brief);
        for (i = 0; i < pstr->argc; i++)
        {
            if (pstr->argv[i])
                printf("|| @ para%d\t| @ %s \n", i, pstr->argv[i]);
        }
        printf("----------------------------------------------------------------------------\n");
		/* B[执行]:执行结束 */
        
        pstr++; /*C[移动]:这一步还是移动,属于复杂场景,是六种排列的延伸。。。*/
    } else {
        break}
}

7 总结

文章已经很长了,最后不废话,直接上表格。

编号顺序合适的循环语句
1A -> B -> Cfor/while
2A -> C -> Bwhile
3B -> C -> Ado-while
4C -> B -> Ado-while
5B -> A -> Cwhile-break
6C -> A -> Bwhile-break
x复合场景while-break

恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-06 09:26:01  更:2021-08-06 09:26:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 3:47:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码