c控制语句:循环

目录

再探while循环

程序注释

c风格读取循环

while语句

终止while循环

何时终止循环

while:入口条件循环

语法要点

用关系运算符和表达式比较大小

什么是真

其他真值

新的_Bool类型

优先级和关系运算符

不确定的循环和计算循环

for循环

利用for的灵活性

其他赋值运算符:+= ,-=,*=,/=,%=

逗号运算符

当zeno遇到for循环

出口条件循环:do   while

如何选择循环

嵌套循环

程序分析

嵌套变式

数组简介

​ 在for循环中使用数组

使用函数返回值的循环示例

使用带返回值的函数 

编程练习


再探while循环

程序注释

while循环的测试条件是如下表达式:

status == 1

==运算符是c的相等运算符,该表达式判断status是否等于1。当status的值不为1时,循环结束。

要让程序正常运行,每次循环都要获取num的一个新值,并重置status。程序利用scanf()的两个不同的特性来完成。首先,使用scanf()读取一个num的一个新值;然后,检查scanf()的返回值判断是否成功获取值。scanf()返回成功读取项的数量。如果用户输入的不是数字(如,q),scanf()会读取失败并返回0.

      如何告诉循环何时停止?该程序利用scanf()的双重特性避免了在循环中交互输入时的这个棘手问题。例如,假设scanf()没有返回值,那么每次循环只会改变num的值。虽然可以使用num的值来结束循环,比如把num>0或num!= 0作为测试条件,但是这样用户就不能输入某些值,如-3或0.

      总之,因为while循环是入口条件循环,程序在进入循环体之前必须获取输入的数据并检查status的值,所以在while前面要有一个scanf()。要让循环继续执行,在循环内需要一个读取数据的语句,这样程序才能获取下一个status的值,所以在while循环末尾还要一个scanf(),它为下一次迭代做好了准备。可以把下面的伪代码作为while循环的标准格式:

获得第1个用于测试的值

 当测试为真时

       处理值

       获取下一个值

c风格读取循环

      第二种形式同时使用scanf()的两种特性。首先,如果函数调用成功,scanf()会把一个值存入num,然后,利用scanf()的返回值(0 或 1,不是num的值)控制while循环。因为每次迭代都会判断循环的条件,所以每次迭代都要调用scanf()读取新的值做判断。换句话说,c的语法特性让你可以用下面的精简版替换标准版本:

    当获取值和判断值都成功

            处理该值

while语句

    while循环的通用形式如下:

   while(expression) 

         statement

 statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来的复合语句。

终止while循环

     在构建while循环时,必须让测试表达式的值有变化,表达式最终要为假。否则,循环就不会终止(实际上,可以使用break和if语句来终止循环)

何时终止循环

while:入口条件循环

while循环是使用入口条件循环。所谓“有条件”指的是语句部分的执行取决于测试表达式描述的条件,如(index < 5)。该表达式是一个入口条件,因为必须满足条件才能进入循环体。在下面的情况中,就不会进入循环体,因为条件一开始就为假:

index = 10;

while( index++ < 5)

{

      printf("have a fair day or better.\n");

}

语法要点

     使用while时,要牢记一点:只有在测试条件后面的单独语句(单独语句或复合语句)才是循环部分。

用关系运算符和表达式比较大小

     while循环经常依赖测试表达式做比较,这样的表达式被称为关系表达式,出现在关系表达式中间的运算符叫关系运算符。如下图:

关系运算符
运算符含义
<小于
<=小于或等于
==等于
>=大于或等于
>大于
!=不等于

什么是真

       在c中,表达式一定有一个值,关系表达式也不例外。

      由此可知,对于c语言,表达式为真的值是1,表达式为假的值是0。一些c程序使用下面的循环结构,由于1为真,所以循环会一直进行。

while(1)

{

......

}

其他真值

     

          一般而言,所有的非零值都视为真,只有0被视为假。在c中,真的概念还真宽!也可以说,只要测试条件的值为非零,就会执行while循环。这是从数值方面而不是从真/假方面来看测试条件。要牢记:关系表达式为真,求值得1;关系表达式为假,求值得0。因此,这些表达式实际上相当于数值。

          如果待比较得一个值是常量,可以把该常量放在左侧有助于编译器捕获错误:

           5 = canoes   //语法错误

           5 == canoes   //检查 canoes得值是否为5

           可以这样做是因为c语言不允许给常量赋值, 编译器会把赋值运算符的这种用法作为语法错误标记出来。无多经验丰富的程序员在构建比骄是否相等的表达式时,都习惯把常量放在左侧。

           总之,关系运算符用于构成关系表达式。关系表达式为真时值为1,为假时值为0。通常用关系表达式作为测试条件的语句(如while和if)可以使用任何表达式作为测试条件,非零为真,零为假。

新的_Bool类型

       在编程中,表示真或者假的变量被称为布尔变量。所以_Bool是c语言中布尔变量的类型名。_Bool类型的变量只能存储1(真)或0(假)。如果把其他非零数值赋给_Bool类型的变量,该变量会被设置为1。这反映了c把所有的非零值都视为真。

优先级和关系运算符

       关系运算符的优先级比算术运算符低。比赋值运算符高。这意味着x >  y + 2和x > (y + 2)相同,x = y > 2和 x = (y > 2)相同。换言之,如果y大于2,则给x赋值1,否则赋值0,y的值不会赋值给x。

      关系运算符比赋值运算符的优先级高,因此,x_bigger = x > y;相当于x_bigger = (x>y)。与其他运算符一样,关系运算符的结合律也是从左往右。因此:

    ex != wye  == zee  与 (ex !=wye) == zee 相同

    首先,c判断ex与wyx是否相等;然后,用得出的值1或0(真或假)再与zee比较。

不确定的循环和计算循环

      一些while循环是不确定循环,所谓不确定循环,指在测试表达式为假之前,预先不知道要执行多少次循环。另外,还有一类是计数循环。这类循环之前就知道要重复执行多少次。

       在创建一个重复执行固定次数的循环中涉及了3个行为:

       1.必须初始化计数器;

        2.计数器与有限的值作比较;

        3.每次循环时递增计数器;

       while循环的测试条件比较,递增运算符执行递增。递增发生在循环的末尾,这可以防止不小心漏掉递增。因此,这样做比将测试和更新组合放在一起要好,但是计算器的初始化放在循环外,就有可能忘记初始化。

for循环

        for循环把上述3个行为(初始化,测试和更新)组合在一起。

           关键字for后面的圆括号中有3个表达式,分别用两个分号隔开。第1个表达式是初始化,只会在for循环开始时执行一次。第2个表达式是测试条件,在执行循环之前对表达式求值。如果表达式为假,循环结束。第3个表达式执行更新,在每次循环结束时求值。

      for循环的第1行包含了循环所需的所有信息:num的初值,num的终值和每次循环num的增量。

利用for的灵活性

       以前面程序示例中的for循环为例,第1个表达式给计数器赋初值,第 2个表达式表示计数器的范围,第3个表达式递增计数器。除此之外,for循环还有其他9种用法。

      --------可以使用递减运算符来递减计数器

            --------可以让计数器递增2,10等

          -------可以用字符代替数字计数

       该程序能正常运行是因为字符在内部是以整数形式存储的,因此该循环实际上仍是用整数来计数。

          ----------除了测试迭代次数外,还可以测试其他条件。在for_cube程序种,可以把:

           for(num = 1; num <= 6; num ++)

           替换成:

           for(num = 1; num*num*num <= 216; num++)

           如果与控制循环次数相比,你更关心限制立方体的大小,就可以使用这样的测试条件。

         ----------可以让递增的量几何增长,而不是算术增长。也就是说,每次都乘上而不是加上一个           固定的量:

           ------可以省略一个或多个表达式(但是不能省略分号),只要在循环中包含能结束循环的语句即可。

     如果省略第2个表达式被视为真,所以下面的循环会一直运行。

      for(;;)

         printf("I want some action\n");

其他赋值运算符:+= ,-=,*=,/=,%=

       c有许多赋值运算符。最基本最常用的是=,它把右侧表达式的值赋给左侧的变量。其他赋值运算符都用于更新变量。其用法都是左侧是一个变量名,右侧是一个表达式。赋给变量的新值是根据右侧表达式的值调整后的值。确切的调整方案取决于具体的运算符。例如:

                         score +=20  与 socres  = socres + 20 相同

                        dimes  -=2    与 dimes  = dimes - 2     相同

                        bunnies *= 2  与 bunnies = bunnies *2 相同

                        time   /= 2.73  与 time = time /2.73相同

                          reduce %= 3   与  reduce = reduce % 3  相同

还可以使 用更复杂的表达式,例如:

  x *= 3 * y +12与 x = x *(3*y +12)相同

       并非一定要使用这些组合形式的赋值运算符。但是,它们让代码更加紧凑,而且与一般形式相比,组合形式的赋值运算符生成的机器代码更高效。当需要在for循环中塞进一些复杂的表达式时,这些组合的赋值运算符特别有用。

逗号运算符

      逗号运算符扩展了for循环的灵活性,以便在循环中包含更多的表达式。如下程序演示了一个打印一类邮件资费的程序(邮资为首重46美分/盎司,续重20美分/盎司)

        逗号运算符并局限在for循环中使用,但是这是它最常用的地方。 逗号运算符有两个其他性质。首先,它保证了被分隔的表达式从左往右求值(换言之,逗号是一个序列点,所有逗号左侧项的所有副作用都在程序执行逗号右侧项之前发生)。因此,ounces在cost之前被初始化。在该例中,顺序并不重要。

        其次,整个逗号表达式的值是右侧项的值。例如,下面语句

         x = (y = 3, (z = ++y + 2) + 5);

         先把3赋给y,递增y为4,然后把4加2之和(6)赋给z,接着加上5,最后把结果11赋给x。假设在写数字时不小心输入了逗号:

         houseprice =  249,500;

          这不是语法错误,c编译器会将其解释为一个逗号表达式,即houseprice = 249 是逗号左侧的子表达式,500是右侧的子表达式。因此,整个逗号表达式的值是逗号右侧表达式的值,而且左侧的赋值表达式把249赋给变量houseprice。因此,这与下面代码的效果相同:

         houseprice = 249;

         500;

         任何一个表达式后面加上一个分号就成为了表达式语句。所以,500;也是一条语句,但是什么也不做。下面的语句:

         houseprice = (249,500);

         赋给houseprice的值是逗号右侧子表达式的值,即500.

         逗号也可以用作分隔符。在下面语句中的逗号都是分隔符,不是逗号运算符:

          char ch,date;

          printf("%d %d\n",chimps,chumps);

当zeno遇到for循环

       希腊哲学家提出zeno曾经提出箭永远不会达到它的目标。首先,他认为箭要到达目标的一半,然后再到达剩余距离的一半,然后继续到达剩余距离的一半,这样就无穷无尽。zeno认为箭的飞行过程有无数个部分,所以要花费无数时间才能结束这一过程。

       我们采用一种定量的方法,假设箭用1秒走完一半的路程,然后用1/2秒走完剩余距离的一半,然后用1/4再走完剩余距离的一半。无限序列来表示总时间:

1 + 1/2 + 1/4 + 1/8 + 1/16 + ....

          尽管不断添加新的项,但是总和看起来变化不大。

出口条件循环:do   while

         while循环和for循环都是入口条件循环,即在循环的每次迭代之前检查测试条件,所以有可能根本不执行循环体中的内容。c语言还有出口条件循环,即在循环的每次迭代之后检查测试条件,这保证了至少执行循环体中的内容一次。这种循环被称为do  while循环。

       下面是do   while循环的通用形式:

do

       statement

while(expression);

       statement可以是一条简单语句或复合语句。注意,do   while循环以分号结尾。

        do   while循环在执行完循环体后才执行测试条件,所以至少执行循环体一次;而for循环或while循环都是在执行体之前先执行测试条件。do   while循环适用于那些至少要迭代一次的循环。例如,包含do  while循环的密码程序伪代码 

       do

       {

               提示用户输入密码

               读取用户输入的密码

       }while(用户输入的密码不等于密码);

       其他行为;

如何选择循环

     如何选择使用哪一种循环?首先,确定是需要入口条件循环还是出口条件循环。通常,入口条件用得比较多,有几个原因。其一,一般原则是在执行循环之前测试条件比较好。其二,测试放在循环的开头,程序的可读性更高。另外,在许多应用中,要求在一开始不满足测试条件时就直接跳过整个循环。

      假设需要一个入口条件循环,用for循环还是while循环?这取决于个人喜好,因为二者皆可。要让for循环看起来像while循环,可以省略第1个和第3个表达式。例如:

       for(   ; test  ;   )

       与下面的while效果相同

        while(   test   )

       要让while循环看起来像for循环,可以在while循环的前面初始化变量,并在while循环中包含更新语句。例如:

         初始化;

         while(测试)

        {

                其他语句

               更新语句

        }

        与下面的for循环效果相同:

         for(初始化;测试;更新)

                其他语句

        一般而言,当循环涉及初始化和更新变量时,用for循环比较合适,而在其他情况下用while循环更好。对于下面这种条件,用while循环就很合适:

        while(   scanf("%ld", &num)  ==  1)

        对于涉及索引计数的循环,用for循环更合适。例如:

        for( count = 1; count <= 100 ; count++)

嵌套循环

       嵌套循环指在一个循环内包含另外一个循环。嵌套循环常用于按行和列显示数据,也就是说,一个循环处理一行中的所有列,另一个循环处理所有的行。

     

程序分析

          外层循环从row为0开始循环,到row为6循环结束。因此,外层循环要执行6次,row的值从0变为5.每次迭代要执行的第1条语句是内层的for循环,该循环要执行10次,在同一行打印字符A~J;第2条语句是外层循环的printf("\n");,该语句的效果是另起一行,这样在下一次运行内存循环时,将在下一行打印的字符。

          注意,嵌套循环中的内层循环在每次外层循环迭代时都执行完所有的循环。

嵌套变式

          内层循环和外层循环所做的事情相同。可以通过外层循环控制内层循环,在每次外层循环迭代时内层循环完成不同的任务。   内层循环开始打印的字符取决于外层循环的迭代次数。

数组简介

         数组是按顺序存储的一系列类型相同的值,如10个char类型的字符或15个int类型的值。整个数组有一个数组名,通过整数下标访问数组中单独的项或元素。例如,以下声明:

         float debts[20];

         声明debts是一个内含20个元素的数组,每次元素都可以存储float类型的值。数组的第1个元素是debts[0],第2个元素是debts[1],一次类推,直到debts[19]。注意,数组元素的编号从0开始,不是从1开始。可以每个元素赋float类型的值。

         考虑到影响执行的速度,c编译器不会检查数组的下标是否正确。下面的代码,都不正确:

         debts [20]  = 88.32 //该数组元素不存在

         debts [33]  = 828.12;//该数组元素不存在

         编译器不会查找这样的错误。当运行程序时,这会导致数据被放置在已被其他数据占用的地方,可能会破坏程序的结果甚至导致程序异常中断。

        可以把字符串存储在char类型的数组中(一般而言,char类型数组的所有元素都存储char类型的值)。如果char类型的数组末尾包含一个表示字符串末尾的空字符\0,该数组中的内容就构成了一个字符串。

       用于识别数组元素的数字被称为下标,索引或偏移量。下标必须是整数,而且要从0开始计数。数组的元素被依次存储在内存中相邻的位置

 在for循环中使用数组

         读取10个高尔夫分数,稍后进行处理。使用数组,就不用创建10个不同的变量来存储10个高尔夫分数。而且,还可以用for循环来读取数据。程序打印总分,平均分,差点,它是平均分与标准分的差值)

       由于scanf()会跳过空白符,所以可以在一行输入10个数字,也可以每行只输入一个数字,或者混合使用空格和换行符隔开每个数字(因为输入是缓冲的,只有当用户键入Enter键后数字才会被发送给程序。)

使用函数返回值的循环示例

       power()函数的原型,它声明程序将使用一个名为power()的函数。开头的关键字double表明power()函数返回一个double类型的值。编译器要知道power()函数返回值的类型,才能知道有多少字节的数据,以及如何解释它们。这就是为什么声明函数的原因。

使用带返回值的函数 

       声明函数,调用函数,定义函数,使用关键字return,都是定义和使用带返回值函数的基本要素。

      既然在使用函数返回值之前要声明函数,那么为什么在使用scanf()的返回值之前没有声明scanf()?为什么在定义中说明了power()的返回类型为double,还要单独声明这个函数?

      编译器在程序中首次遇到power()时,需要知道power()的返回类型。此时,编译器尚未执行到power()的定义,并不知道函数定义中的返回类型是double。因此,必须通过前置声明预先说明函数的返回类型。前置声明告诉编译器,power()定义在别处,其返回类型为double。如果把power()函数的定义置于main()的文件顶部,就可以省略前置声明,因为编译器在执行到main()之前已经知道power()的所有信息。但是,这不是c的标准风格。因为main()通常只提供整个程序的框架,最好把main()放在所有函数定义的前面。另外,通常把函数放在其他文件中,所以前置声明必不可少。

        为什么不用声明scanf()函数就可以使用它?其实,已经声明了。stdio.h头文件中包含了scanf(),printf()和其他I/O函数的原型。scanf()函数的原型表明,它返回的类型是int。

编程练习

1.编写一个程序,创建一个包含26个元素的数组,并在其中存储26个小写字母。然后打印打印数组的所有内容。

  2.使用嵌套循环,按下面的格式打印字符:

$
$$
$$$
$$$$
$$$$$

 使用嵌套循环,外层循环控制打印的行数,内层循环控制打印行的内容。内层循环打印的个数随着外层循环递增。

3. 使用嵌套循环,按下面 的格式打印字母:

F
FE
FED
FEDC
FEDCB
FEDCBA

使用嵌套循环打印,外层循环控制打印的行数,内层循环控制打印的内容,打印的个数随着外层循环递增。同时打印的第一个内容都是F,说明每次都要初始化为F,可以使用逗号运算符。

4.使用嵌套循环,按下面的格式打印字母:

A
BC
DEF
GHIJ
KLMNO
PQRSTU

使用嵌套循环,外层循环控制打印的行数,内存循环控制打印的内容,打印的内容的值ASCII依次递增,可以使用逗号运算符。

 5.编写一个程序,提示用户输入大写字母。使用嵌套循环以下面金字塔的格式打印字母:

        A
      ABA
    ABCBA
  ABCDCBA
ABCDEDCBA

打印这样的图形,要根据用户输入的字母来决定。例如,上面的图形是在用户输入E后的打印结果。提示:用外层循环处理行,每行使用3个内层循环,分别处理空格,以升序打印字母,以降序打印字母。

申明两个计数器i,j。计算器i用于控制外层循环,计数器用于控制内层循环。以升序打印字母的时候每次都需要初始化从A开始打印,可以使用逗号表达式。

 6.编写一个程序打印一个表格,每一行打印一个整数,该数的平方,该数的立方。要求用户输入表格的上下限。使用一个for循环

7.编写一个程序把一个单词读入一个字符数组中,然后倒序打印这个单词。提示:strlen()函数可用于计算数组最后一个字符的下标。

strlen()函数来确定字符数组的有效长度,通过返回值来确定下标,注意循环的起始位置是单词长度-1,循环的终止位置是0。 最后一个字符的下标 = strlen(array) - 1 。

 8.编写一个程序,要求用户输入两个浮点数,并打印两数之差除以两数乘积的结果。在用户输入非数字之前,程序应循环处理用户输入的每对值。

循环的入口条件scanf函数成功读取浮点型数据的个数

11.编写一个程序,在数组中读入8个整数,然后按倒序打印这8个整数。

倒序打印的时候需要注意,最后一个元素的下标为n-1

 

15.编写一个程序,读取一行输入,然后把输入的内容倒序打印出来。可以把输入存储在char类型的数组中,假设每行字符不超过255。回忆一下,根据%c转换说明,scanf()函数一次只能从输入中读取一个字符,而且在用户按下enter键时scanf()函数会生成一个换行字符(\n)。

在使用%c转换说明符时,如果scanf()遇到用户输入回车符,会自动生成换行符(\n),因此可以使用换行符来判断用户输入完毕。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值