C语言有三种循环结构:while、for和do while。下面依次讲解

一、while循环

1、while介绍

while(逻辑表达式) //布尔值为1 真
{
反复执行的语句
}

只要逻辑表达式结果为真就反复不停执行大括号里的语句,直到逻辑表达式结果为假循环结束
只要把逻辑表达式写成1则循环成为死循环,while(1)
while循环里可以使用break和continue;

break 关键字很重要,表示终止本层循环。现在这个例子只有一层循环,当代码执行到break 时,循环便终止。
如果把 break 换成 continue 会是什么样子呢? continue 表示终止本次(本轮) 循环。当代码执行到 continue 时,本轮循环终止,进入下一轮循环。while( 1)也有写成 while(true) 或者 while(1==1) 或者 while((bool) 1)等形式的,效果一样。

还需注意:任何非零值都是真.比如,while (-1) == while (true)

2、while复合语句

复合语句是使用花括号组织起来的两个或更过的语句,它也被称为一个代码块。

例如:

/*程序1*/

index = 0;
while (index++ < 10)sam = 10 *index +2;
printf ("sam = %d\n", sam);

/*程序2*/

index = 0;
while (index++ < 10)
{sam = 10 *index +2;printf ("sam = %d\n", sam);
}

说明:在没有花括号的情况下,while循环语句的范围是从while到下一个分号。

3、技巧

A、假如你想要跳过输入直到第一个不为空格或者数字的字符,你可以使用这样的循环:

while (scanf ("%d", &num) == 1) {....}

只要scanf()输入一个整数,它就返回1,循环就会继续。

B、while ((ch = getchar ()) != '\n') {....}

只要输入不为换行符'\n',它就返回1,循环就会继续。

C、while循环内只执行一次的语句

int i = 0;

while (1)

{

if (0 == i)

{只执行一次该语句};

i == 1;

}

4、空语句

while ((ch = getchar ()) != EOF && ch != '\n')
;

while语句之后的单独一个分号称为空语句,它就是应用于目前这个场合,也就是语法要求这个地方出现一条语句但又无需执行任何任务的时候,这个分号独占一行,这是为了防止读者错误地以为接下来的语句也是循环的一部分。

二、do while循环

1、do while介绍

do{
}  while(为真的逻辑);

do while为退出条件循环,判断条件在执行循环之后进行检查,这样就可以保证循环体中的语句至少被执行一次。

2、do while复合语句

例如:

do scanf ("%d", &num);
while (num != 20);  /*注意while后面有分号 ;*/

3、do while的常规用法

[cpp] view plaincopy
  1. //限制输入,必须输入0到10的数,否则重新输入。
  2. #include"stdio.h"
  3. int main()
  4. {
  5. int num;
  6. do
  7. {
  8. printf("请输入0到10的整数:"); //先进行输入提示
  9. scanf("%d",&num); //从键盘读取一个数
  10. getchar();
  11. }while (num > 10 || num < 0);  //如果这个数不再0~10内,也就是小于0或者大于10,重新输入
  12. printf("输入正确,输入的数为:%d\n", num); //输入正确则跳出循环并给出提示,\n表示换行
  13. }

4、do {}while(0)的用法

参看:do while的使用
linux内核中 do{}while(0)用法分析

linux内核和其他一些开源的代码中,经常会遇到这样的代码:

do{...
}while(0)

这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,那么为什么要这么用呢?

实际上,do{...}while(0)的作用远大于美化你的代码,while(0)的好处在于,在编译的时候,编译器基本都会为你做好优化,把这部分内容去掉,最终编译的结果不会因为这个do while而导致运行效率上的差异。。查了些资料,总结起来这样写主要有以下几点好处:

1、辅助定义复杂的宏,避免引用的时候出错:

举例来说,假设你需要定义这样一个宏:

#define DO_SOMETHING()\foo1();\foo2();

这个宏的本意是,当调用DO_SOMETHING()时,函数foo1()和foo2()都会被调用。但是如果你在调用的时候这写:

if(a>0)DO_SOMETHING();

因为宏在预处理的时候会直接被展开,你实际上写的代码是这个样子的:

if(a>0)foo1();
foo2();

这就出现了问题,因为无论a是否大于0,foo2()都会被执行,导致程序出错。

那么仅仅使用{}将foo1()和foo2()包起来行么?

我们在写代码的时候都习惯在语句右面加上分号,如果在宏中使用{},代码里就相当于这样写了:“{...};”,展开后就是这个样子:

if(a>0)
{foo1();foo2();
};

注意if{};后面的";",如果if后面还有else等语句则编译不会通过。所以,很多人才采用了do{...}while(0);

#define DO_SOMETHING() \do{ \foo1();\foo2();\}while(0)\
...if(a>0)DO_SOMETHING();...

这样,宏被展开后,才会保留初始的语义。GCC提供了Statement-Expressions用以替代do{...}while(0); 所以你也可以这样定义宏:

#define DO_SOMETHING() ({\foo1(); \foo2(); \
})

http://www.spongeliu.com/ 
2、避免使用goto对程序流进行统一的控制:

有些函数中,在函数return之前我们经常会进行一些收尾的工作,比如free掉一块函数开始malloc的内存,goto一直都是一个比较简便的方法:

int foo()
{somestruct* ptr = malloc(...);dosomething...;if(error){goto END;}dosomething...;if(error){goto END;}dosomething...;END:free(ptr);return 0;}

由于goto不符合软件工程的结构化,而且有可能使得代码难懂,所以很多人都不倡导使用,那这个时候就可以用do{}while(0)来进行统一的管理:

int foo()
{somestruct* ptr = malloc(...);do{dosomething...;if(error){break;}dosomething...;if(error){break;}dosomething...;}while(0);free(ptr);return 0;}

这里将函数主体使用do()while(0)包含起来,使用break来代替goto,后续的处理工作在while之后,就能够达到同样的效果。

3、避免空宏引起的warning

内核中由于不同架构的限制,很多时候会用到空宏,在编译的时候,空宏会给出warning,为了避免这样的warning,就可以使用do{}while(0)来定义空宏:

#define EMPTYMICRO do{}while(0)

4、定义一个单独的函数块来实现复杂的操作:

当你的功能很复杂,变量很多你又不愿意增加一个函数的时候,使用do{}while(0);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。

5、do {}while(0)的简化

#define st(x) do { x } while (__LINE__ == -1)

1, __LINE__ 是个宏,它代表当前代码在源文件的行号,它是大于0的,所以__LINE__ == -1 等同于0,化简为:
#define st(x) do { x } while (0)
2,do {} while (0)通常用于宏中, 为的是避免如下情况:
#define st(x) x
那么我们在调用 if (0) st(a = b; b = c;) 时会被解释成
if(0)
a = b;
b = c;
可见 if 只对a = b;起作用。

三、for循环
1、for介绍
for(num=1; num <10; num++);
在关键字for之后的圆括号中包含了由两个分号分开的三个表达式。
第一个表达式进行初始化,它在for循环开始的时候执行一次。
第二个表达式是判断条件,在每次执行循环之前都要对它进行求值。当表达式为假时,循环结束。
第三个表达式进行改变或者称为更新,它在每次循环结束时进行计算。
2、for复合语句
例如:
for (n = 0; n < 10; n+)printf ("%d %d\n", n, 2 * n + 1);
3、for循环的灵活性
A、可以让一个或多个表达式为空(但是不要遗漏分号)。只须确保在循环中包含一些能是循环最终结束的语句。
B、顺便说一句,中间的那个控制表达式为空会被认为是真,所以下面的循环会永远执行:
for (; ;)
printf ("hello world\n");
C、第一个表达式不必初始化一个变量,它也可是某种类型的printf()语句,要记住第一个表达式只在执行循环的其他部分之前被求值或执行一次。
D、for循环可使用逗号运算符把两个表达式链接为一个表达式,并保证最左边的表达式最先计算。
例如:
for (n = 2, m = 0; m < 1000; n *=2)m +=n;
E、在for循环中使用数组,使用#define来指定数组大小
例如:
#define SIZE 10
for (int n = 0; n < SIZE; n++)printf ("hello world\n");
4、嵌套循环
A、嵌套循环是指在另一个循环之内的循环。通常使用嵌套循环来按行按列显示数据。也就是说一个循环处理一行中的所有列,而另一个循环处理所有的行。
B、外层for循环为外部循环,按行显示数据;内层for循环为内部循环。按列显示数据。
在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。例如:
长循环在最内层,效率高
for (col=0; col<5; col++ )
{for (row=0; row<100; row++){sum = sum + a[row][col];}
}
长循环在最外层,效率低
for (row=0; row<100; row++)
{for ( col=0; col<5; col++ ){sum = sum + a[row][col];}
}

C、建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。半开半闭区间写法和闭区间写法虽然功能是相同,但相比之下,半开半闭区间写法写法更加直观。

半开半闭区间写法:for (n = 0; n < 10; n++) {....}
闭区间写法:for (n = 0; n <= 9; n++) {....}
D、不能在 for 循环体内修改循环变量,防止循环失控。
for (n = 0; n < 10; n++)
{…n = 8;//不可,很可能违背了你的原意…
}

E、循环要尽可能的短,要使代码清晰,一目了然。

如果你写的一个循环的代码超过一显示屏,那会让读代码的人发狂的。解决的办法由两个:第一,重新设计这个循环,确认是否这些操作都必须放在这个循环里;第二,将这些代码改写成一个子函数, 循环中只调用这个子函数即可。 一般来说循环内的代码不要超过 20行。
F、把循环嵌套控制在 3 层以内。
国外有研究数据表明,当循环嵌套超过 3 层,程序员对循环的理解能力会极大的降低。如果你的循环嵌套超过 3 层,建议你重新设计循环或是将循环内的代码改写成一个字函数。
G、扩展
1、编程实现打印一个空心的菱形和实心的菱形:
         *
      *****
   *********
*************
   *********
      *****
         *
#include <stdio.h>
int main()
{int i=0,j=0,k=0;for(i=0;i<=3;i++){for(j=0;j<=5-2*i;j++){printf(" ");}for(k=0;k<=4*i;k++){printf("*");}printf("\n");}for(i=0;i<=2;i++){for(j=0;j<=2*i+1;j++){printf(" ");}for(k=0;k<=8-4*i;k++){printf("*");}printf("\n");}return 0;
}

2、请简述以下两个 for 循环的优缺点

// 第一个
for (i=0; i<N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}

优缺点:效率低但程序简洁
// 第二个
if (condition)
{
for (i=0; i<N; i++)
DoSomething();
}
else
{
for (i=0; i<N; i++)
DoOtherthing();
}

优缺点:效率高但程序不简洁

说明:第一个程序比第二个程序多执行了 N-1 次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果 N 非常大,最好采用第二个程序的写法,可以提高效率。如果 N 非常小,两者效率差别并不明显,采用第一个程序的写法比较好,因为程序更加简洁。
注意:
1、上面有提到,在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。  
2、建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。
int a[10];
半开半闭区间写法:for (n = 0; n < 10; n++)
闭区间写法:for (n = 0; n <= 9; n++)

半开半闭区间写法和闭区间写法虽然功能是相同,但考虑到边界计算,例如我们要在C语言中定义一个拥有 10 个元素的数组,那么 0 就是数组下标的第一个 “入界点”(指处于数组下标范围以内的点,包括边界点),而 10 就是数组下标中的第一个“出界点”(指不在数组下标范围以内的点,不含边界点)。因此写成半开半闭区间的写法更加直观。

3、不能在 for 循环体内修改循环变量,防止循环失控。

for (n = 0; n < 10; n++)
{
…
n = 8;//不可,很可能违背了你的原意
…
}

4、循环要尽可能的短,要使代码清晰,一目了然。
如果你写的一个循环的代码超过一显示屏,那会让读代码的人发狂的。解决的办法由两个:第一,重新设计这个循环,确认是否这些操作都必须放在这个循环里;第二,将这些代码改写成一个子函数, 循环中只调用这个子函数即可。 一般来说循环内的代码不要超过 20行。

5、把循环嵌套控制在 3 层以内。
国外有研究数据表明,当循环嵌套超过 3 层,程序员对循环的理解能力会极大的降低。如果你的循环嵌套超过 3 层,建议你重新设计循环或是将循环内的代码改写成一个字函数。

C语言再学习 -- 循环语句相关推荐

  1. C语言再学习--关键字

    如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/53021879 C语言一共有32个关键字,如下表所示: 关键字 说明 auto ...

  2. C语言再学习 -- 详解C++/C 面试题 2

    (经典)C语言测试:想成为嵌入式程序员应知道的0x10个基本问题. 参看:嵌入式程序员面试问题集锦 1.用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define ...

  3. C语言再学习 -- 详解C++/C 面试题 1

    参看:<高质量C++ C编程指南>.林锐 对这篇文章记忆犹新,因为之前找工作面试的时候,遇到过一家公司就是用的这套面试题.现在就结合考查的知识点和我总结完 C 语言再学习后的深入理解,来详 ...

  4. 【C语言进阶深度学习记录】七 C语言中的循环语句

    文章目录 1 循环语句分析 1.1 do...while循环 1.2 while循环 1.3 for循环 1.4 三种循环语句使用对比 2 break和continue的区别 3 总结 1 循环语句分 ...

  5. C语言再学习 -- 再论数组和指针

    之前有总结指针数组,但是现在看来总结的太简单了.好多重要的知识点都是一带而过的.本想在后面添加后来想想算了,还是再写一篇文章来详细介绍数组和指针这对冤家吧. 之前总结的,参看:C语言再学习 -- 数组 ...

  6. C语言再学习 -- 时间函数

    在软件设计中经常会用到关于时间的处理,用来计算语句.函数的执行时间,这时就需要精确到毫秒甚至是微妙的时间.我们首先来介绍一下,时间单位: 时间单位还有:秒(s).毫秒(ms).微秒 (μs).纳秒(n ...

  7. C语言再学习 -- 关键字volatile

    上周确实事情挺多的,年会.公司聚餐,一到过年就有忙不完的事分心.还好C语言再学习总结的已经差不多了,年前也不展开别的了,接下来这十几天.总结几篇典型的面试题吧. 言归正传,接下来看看关键字 volat ...

  8. C语言再学习 -- 关键字const

    const 关键字其实我们并不陌生,之前有讲过const修饰数组和指针.现在来详细介绍这个关键字. 参看:[C/C++和指针]著名的<const的思考> 一.const 介绍 1.cons ...

  9. C语言再学习 -- 关键字typedef

    参看:C语言再学习 -- 结构和其他数据形式 参看:C语言再学习 -- 关键字struct(转) 参看:常见typedef 用法 参看:关于typedef的用法总结 一.typedef 介绍 type ...

最新文章

  1. 什么是分布式系统!以及分布式系统架构的优缺点!
  2. 一年的第几周怎么算_部编版一年级下册第7课《怎么都快乐》图文讲解+知识点梳理...
  3. [Linux].netrc或者_netrc使用可以
  4. 普中stm32开发板tftlcd显示图片_STM32实例TFTLCD介绍
  5. Android官方开发文档Training系列课程中文版:创建自定义View之View的绘制
  6. matlab拟合二元函数图,求助大神。。matlab拟合二元函数,求解系数。函数形式已知...
  7. 计算机图形学实验报告百度云盘,计算机图形学实验报告(一).doc
  8. google earth pro 安装后启动时报错:google 地球无法连接到登录服务器(错误代码:c000000c)
  9. Spring5开发新功能
  10. Beamer Introduction
  11. 数据中台在企业数字化转型中的践行(上篇)
  12. 红帽的PAAS平台openshift
  13. 打包时错误 Entry name ‘classes.dex‘ collided 的解决办法
  14. 照片OCD问题,滑动窗口分类器,上限分析
  15. 利用脚手架创建vue项目时报这个错误,哪位大神帮忙解答一下,实在是找到解决办法了。万分感谢
  16. 点下确认的那一刻,我的大脑在发生些什么?
  17. 「数字货币监管」听证会重磅来袭,无形之笼悄然降临?
  18. AD模数转换模块diy(AD9236/12位/80MSPS)
  19. 【VisionMaster】二次开发之第三方库的使用
  20. 我参加了资金盘培训,get了这些“知识点”

热门文章

  1. 整体C#与Sql培训内容及结构
  2. 死锁的四个必要条件,及处理方法
  3. 用Python做自然语言处理必知的八个工具【转载】
  4. [CHM]果壳中的XAML(XAML in a Nutshell)
  5. 征值和特征向量的几何意义、计算及其性质
  6. 学习Matlab强大的符号计算(解方程)
  7. matlab在图像调用Gabor滤波
  8. 【流量】一觉醒来发现CSDN博客访问量增加十倍!原来是这个原因
  9. [云炬ThinkPython阅读笔记]3.3 构建
  10. [云炬python3玩转机器学习笔记] 2-6关于回归和分类