第7章 C控制语句:分支和跳转

发布于 2021-05-01  676 次阅读


7.1 if语句

问题:读取一列数据,每个数据都表示每日的最低温度(℃),然后打印统计的总天数和最低温度在0℃以下的天数占总天数的百分比。程序中的需要你换通过scanf()读入温度值。while循环每迭代一次,就增加天数,其中if语句负责判断0℃以下的温度并单独统计相应的天数。

//colddays.c    --找出0℃以下的天数占总天数的百分比
#include<stdio.h>
int main(void)
{
    const int FREEZING = 0;
    float temperature;
    int cold_days = 0;
    int all_days = 0;

    printf("Enter the list of daily low temperatures.\n");
    printf("Use Celsius,and enter q to quit.\n");
    while(scanf("%f",&temperature) == 1)
    {
    all_days++;
    if(temperature < FREEZING)
    {
    cold_days++;
    }
    }
    if(all_days!=0)

    printf("%d days total:%.lf%%were below freezing.\n",all_days,100.0*(float)cold_days/all_days);
    if(all_days==0)

    printf("No data entered!\n");

    return 0;
}

if语句被称为分支语句(branching statement)或者选择语句(select statement).

7.2 if else 语句

file

7.2.1 getchar()和putchar()

输入字符,scanf()和printf()根据%c读写字符。
getchar()函数不带任何参数,他从输入队列中返回下一个字符。例如:
下面语句读取下一个字符输入,并把该字符的值赋给变量ch;

ch = getchar();
//与之等价
scanf("$c",&ch);

putchar()函数打印它的参数。例如:
下面语句把之前赋给ch的值作为字符打印出来:

putchar(ch);
//与之等价
scanf("%c",ch);

由于这两个函数只处理字符,因此它比scanf和printf更快、更简洁。因为只处理字符,因此不需要转换说明。其定义在stdio.h头文件中(它们通常是预处理宏,而不是真正的函数)
编程问题:该程序把一行输入重新打印出来,但是每个非空格都被替代成原字符在ASCII序列中的下一个字符,空格不变。“如果字符是空白,原样打印;否则,打印原字符在ASCII序列中的下一个字符”。

//  cypherl.c   --更改输入,空格不变
#include<stdio.h>
#define SPACE ' '
int main(void)
{
    char ch;

    ch = getchar();
    while(ch !='\n')  //如果未换行
    {
        if(ch == SPACE)
            putchar(ch);
        else
        {
            putchar(ch+1);
        }
        ch=getchar();
    }
    putchar(ch);
    return 0;
}
//  加密通话,另外一种写法~
#include<stdio.h>
#define SPACE ' '
int main(void)
{
    char ch;

    while((ch=getchar())!='\n')
    {
        if(ch == SPACE)
        {
            putchar(ch);
        }
        else
        {
        putchar(ch+5);
        }
    }
    return 0;
}
//加密解密代码
#include<stdio.h>
#define SPACE ' '
char jiami(char a);
char jiemi(char a);
int main(void)
{
    int a,i;
    char ch;
    printf("输入要处理的字符:");
    ch = getchar(); 
    printf("请选择你的要干什么。\n");
    printf("按1编码,按2译码:");
    scanf("%d",&a);
    if(a==1)
    {

            putchar(jiami(ch));

    } 
    else if(a==2)
    {

            putchar(jiemi(ch));
}

    printf("\n结束!\n");

    return 0;
}
char jiami(char a)
{
    return (a+5);
}
char jiemi(char a)
{
    return (a-5);
}
//改进
#include<stdio.h>
#define SPACE ' '
char jiami(char a);
char jiemi(char a);
int main(void)
{
    int a,i;
    char ch;
    scanf("%d",&a);
    if(a==1)
    {
        while((ch = getchar())!='\n')
        {
            if(ch==SPACE)
            {
                putchar(ch);
            }
            else
            {
                putchar(ch+5);
            }
        }
    }
    else if(a==2)
    {
        while((ch=getchar())!='\n')
        {
            if(ch==SPACE)
            {
                putchar(ch);
            }
            else
            {
                putchar(ch-5);
            }
        }
    }
    else
    {
        printf("这样子我很难哎~"); 
    }
return 0;
}

7.2.2 ctype.h系列的字符函数

只转换字母,其余符号不变。isalpha()是字母,返回非零值。

//  cypher2.c   --替换输入的字母,非字母字符保持不变
#include<stdio.h>
#include<ctype.h>
int main(void)
{
    char ch;

    while((ch=getchar())!='\n')
    {
    if(isalpha(ch))
        putchar(ch+1);
    else
        putchar(ch);
    }
    putchar(ch);

    return 0;
}
tolower(ch);    //不影响ch的值
ch=tolower(ch); //影响ch的值,把ch转换为小写字母

ctype.h头文件中的字符测试函数。
file

7.2.3 多重选择 else if

问题:多重选择的电费
略。
对于编译器的限制范围,C99标准要求编译器最少支持127层嵌套。
else if原理:

if..
{

}
else 
     if..
     {

     }
else 
     if...
     {

     }
...
//  等价于
if ()
{
...
}
else if()
{
...
}
else if()
{
...
}
else
{

}

7.2.4 else与if配对

规则:
如果没有花括号,else与离他最近的if匹配,除非最近的if被花括号括起来。
file

7.2.5 多层嵌套的if语句

给定一个整数,显示所有能整除它的约数。如果没有约数,则报告该数为一个素数。

提示用户输入数字
当scanf ()返回值为1
    分析该数并报告结果
    提示用户继续输入
for(div = 2;div < num;div++)
    if(num % div == 0 )
    {
    printf("%d is divisible by %d\n",num,div);
    }

该方法有点浪费时间~。如果144%2=0,说明2是144的约数;144除2=72,那么72也是144的一个约数。所以num%div成功,则获得两个约数。
分析后发现,必须测试到的数字到num的平方根就可以了,不用到num。

for(div=2;(div*div)<=num;div++)
    if(num%div == 0)
    {
    printf("%d is divisible by %d and %d.\n",num,div,num/div);
    }

不使用平方根而用这种办法是因为:1.整数乘法比求平方根快。其二,我们还没有正式介绍平方根函数。
如果其为完全平方数

for (div=2;(div*div)<=num;div++)
{
    if(num%div==0)
    {
    if(div*div!=num)
    printf("%d is divisible by %d and %d.\n",num,div,num/div);
    else
    printf("%d is divisible by %d.\n",num,div);
    }
}
//  divisors.c  --使用嵌套if语句显示一个数的约数
#include<stdio.h>
#include<stdbool.h>
int main(void)
{
    unsigned long num;      //待测的数字 
    unsigned long div;      //可能的约数
    bool isPrime;           //素数标记

    printf("Please enter an integer for analysis:");
    printf("Enter q to quit.\n");
    while(scanf("%lu",&num)==1)
    {
        for (div=2,isPrime=true;(div*div)<=num;div++)
        {
            if(num%div==0)
                {
                if((div*div)!=num)
                printf("%lu is divisible by %lu and %lu.\n",num,div,num/div);

            else
                printf("%lu is divisible by %lu.\n",num,div);
                isPrime = false;            //该数不是素数 
            }
         }
     if(isPrime)
     printf("%lu is Prime.\n",num);
     printf("Please enter an integer for analysis:");
    printf("Enter q to quit.\n");
}
    printf("Bye.\n"); 
    return 0;
}

该程序会把1认为是素数,其实它不是。

7.3 逻辑运算符

问题描述:编写一个程序,计算输入的一行句子中除单引号和双引号以外的其他字符的数量。这种情况下可以使用逻辑运算符,并使用句点(.)标识句子的末尾。

//  chcount.c   --使用逻辑与运算符
 #include<stdio.h>
 #define PERIOD '.'
 int main(void)
 {
    char ch;
    int charcount = 0;

    while((ch=getchar())!= PERIOD)
    {
    if(ch!='"'&&ch!='\'')
    charcount++;

    }
    printf("There are %d non-quote characters.\n",charcount);

    return 0;
 }

逻辑运算符:

  1. &&与
  2. ||或
  3. !非
    (练习&&时间)==完美

7.3.1 备选拼写:iso646.h

&& --and
|| --or
! --not

7.3.2 优先级

a>b&&b>c||b>d相当于
((a>b)&&(b>c))||(b>d).

7.3.3 求值顺序

逻辑表达式的求值顺序是从左往右的,一旦发现有使整个表达式为假的因素,立即停止求值。

7.3.4 范围

测试在某个范围:

if(range>=90&&range<=100)
//测试小写字母
if(ch>='a'&&ch<='z')
//对于EBCDIC编码的使用ctype.h的islower()函数
if(islower(ch))

7.4 一个统计单词的程序

问题描述:编写一个统计单词数量的程序(该程序读取并报告单词的数量),该程序能识别并计算这些内容:字符、行数和单词,伪代码如下:

读取一个字符
当有更多的字符输入时
    递增字符计数
    如果读完一行,递增行数计数
    如果读完一个单词,递增单词计数
    读取下一个字符
前面有一个输入循环的模型
 while ((ch = getchar()) != STOP)
 {
    ...
 }
 //改进
 如果c不是空白字符,且inword为假
    设置inword为真,并给单词计数
如果c是空白字符,且inword为真
    设置inword为假
//  wordcnt --统计字符数、单词数、行数
#include<stdio.h>
#include<ctype.h>
#include<stdbool.h>
#define STOP '|'
int main(void)
{
    char c;         //读入字符
    char prev;      //读入的前一个字符
    long n_chars = 0L;  //字符数
    int n_lines = 0;    //行数
    int n_words = 0;    //单词数
    int p_lines = 0;    //不完整的行数
    bool inword = false;    //如果c在单词中,inword 等于true;

    printf("Enter text to be analyzed(| to terminate):\n");
    prev = '\n';      //用于识别完整的行数
    while((c=getchar())!= STOP)
    {
        n_chars++;      //统计字符数
        if(c=='\n')
        {
            n_lines++;  //行数 
         } 
        if(!isspace(c)&&!inword)
        {
            inword = true;  //开始一个新的单词
            n_words++; 
         } 
         if(isspace(c)&&inword)
         {
            inword = false; //到达单词的末尾 
         }
         prev = c;
     } 
     if (prev!='\n')
        p_lines = 1;
    printf("characters = %d,words=%d,lines=%d,",n_chars,n_words,n_lines);
    printf("partial lines = %d\n",p_lines);

    return 0;
 } 

file

7.5 条件运算符:?:

C提供条件表达式(conditional expression)作为表达 if else语句的一种编写方式。?:该运算符分为两部分,需要三个运算对象。三元运算符。
比如:一个数的绝对值

x = (y<0)?-y:y;
//解释
if(y<0)
x=-y;
else
x=y;

把两个值中的最大值赋给变量

max=(a>b)?a:b;

问题描述:计算刷给定平方英尺的面积需要多少罐油漆。(1.7卖2罐)

//  paint.c --使用条件运算符
#include<stdio.h>
#define COVERAGE 350    //每罐油漆可刷面积(平方英尺)
int main(void)
{
    int sq_feet;
    int cans;

    printf("Enter number of square feet to be painted:\n");
    while(scanf("%d",&sq_feet)==1)
    {
        cans=sq_feet/COVERAGE;
        cans +=(sq_feet % COVERAGE==0)?0:1;
        printf("You need %d %s of paint.\n",cans,cans==1?"can":"cans");
        printf("Enter next value (q to quit);\n");
    }

    return 0;
}

file

7.6 循环辅助:continue和break

continue和break语句可以根据循环体中的测试结果来忽略一部分循环内容,甚至结束循环。

7.6.1 continue语句

3种循环都可以使用continue语句。执行到该语句,会跳过本次迭代的剩余部分,并开始下一轮的迭代。

//  skippart.c      --使用continue跳过部分循环
#include<stdio.h>
int main(void)
{
    const float MIN=0.0f;
    const float MAX=100.0f;

    float score;
    float total = 0.0f;
    int n= 0;
    float min = MAX;
    float max = MIN;

    printf("Enter the first score (q to quit):");
    while(scanf("%f",&score)==1)
    {
    if(score<MIN||score>MAX)
    {
    printf("%0.1f is an invalid value.Try again:",score);
    continue;       //跳转到while循环的测试条件
    }
    printf("Accepting %0.1f:\n",score);
    min = (score<min)?score:min;
    max = (score>max)?score:max;
    total+=score;
    n++;
    printf("Enter next score(q to quit):");
    }
    if(n>0)
    {
    printf("Average of %d scores os %0.1f.\n",n,total/n);
    printf("Low = %0.1f,high=%0.1f\n",min,max);
    }
    else
    printf("No valid scores were enterd.\n");
    return 0;
}

有两种办法可以避免使用continue。
一:把剩余块放入else块中
二:用以下格式来代替:

if(score>=0&&score<=100)
{
    /*语句*/
}

continue还可以作占位符。例如:下面的循环读取并丢弃输入的数据,直至读到行末尾;

while(getchar()!='\n')
    continue;

如果使用了continue没有简化代码,

while((ch=getchar())!='\n')
{
    if(ch=='\t')
        continue;
    putchar(ch);
}
//不如
while((ch=getchar())!='\n')
{
    if(ch!='\t')
    putchar(ch);
}

通常,在这种情况下,把if条件的关系反过来可以避免使用continue。

//不包括换行符
count = 0;
while(count<10)
{
    ch = getchar();
    if(ch=='\n')
    continue;
    putchar(ch);
    count++;
}
//包含换行符
count = 0;
for(count = 0; count<10;count++)
{
    ch = getchar();
    if(ch=='\n')
    continue;
    putchar(ch);
}

7.6.2 break语句

程序执行到循环中的break语句时,会终止包含它的循环,并继续执行下一段程序。把skippart.c中的continue换成break时,不是执行下一轮循环,而是导致突出当前循环。如果break语句位于嵌套循环内,它只会影响包含它的循环。
file

break用例:
用一个循环计算矩形的面积,如果用户输入了非数字作为矩形的长或宽,则终止循环。

//  break.c --使用break退出循环
#include<stdio.h>
int main(void)
{
    float length,width;

    printf("Rnter the length of the rectangle:\n");
    while(scanf("%f",&length)==1)
    {
        printf("length = %0.2f:\n",length);
        printf("Enter its width:\n");
        if(scanf("%f",&width)!=1)
        break;
        printf("Width = %0.2f:\n",width);
        printf("Area = %0.2f:\n",length*width);
        printf("Rnter the length of the rectangle:\n");
    }
    printf("Done.\n");
    return 0;
}
//也可以这样控制循环
//while(scanf("%f %f",&length,&width)==2)
//但是break可以方便显示用户输入的值。

和continue一样

while((ch=getchar())!='\n')
{
    if(ch=='\t')
    break;
    putchar(ch);
}
//不如
while((ch=getchar())!='\n'&&ch!='\t')
{
    putchar(ch);
}

break的嵌套

int p,q;

scanf("%d",&p);
while(p>0)
{
    printf("%d\n",p);
    scanf("%d",&q);
    while(q>0)
    {
        printf("%d\n",p*q);
        if(q>100)
            break;  //跳出内层循环
        scanf("%d",&q);
    }
    if(q>100)
        break;      //跳出外层循环
    scanf("%d",&p);
}

7.7 多重选择:switch和break

问题描述:程序读入一个字母,然后打印出该字母开头的动物名。

//  animals.c   --使用switch语句
#include<stdio.h>
#include<ctype.h>
int main(void)
{
    char ch;

    printf("Give me a letter of the alphabet,and I will give an animal name \n beginning with that letter.\n");
    printf("Please type in a letter;type $ to end my act.\n");
    while((ch=getchar())!='#')
    {
        if('\n'==ch)
        continue;
        if(islower(ch)) //只接受小写字母
            switch(ch)
            {
            case'a':
                printf("apple\n");
                break;
            case'b':
                printf("bababababba\n");
                break;
            case'c':
                printf("cat\n");
            default:
                printf("oh no");
            }
        else 
            printf("I recongized only lowercase letters.\n");
        while(getchar()!='\n')
        continue;       //跳过输入行的剩余部分
        printf("Please #\n");
    }
    printf("Bye!\n");
}

7.7.1 switch语句

file
switch在圆括号中的测试表达式的值应为一个整数值(包括char类型)。case标签必须是整数类型(包括char)的常量或整型常量表达式(即,表达式中只包含整型常量)。不能用变量作为case标签。switch的构造如下:

switch(整型表达式)
{
    case 常量1:
        语句;
    case 常量2:
        语句;
    。。。
    default
        语句;
}

7.7.2 只读每行的首字符

animals.c的另外一个独特之处他读取的方式。响应单字符的交互程序中。

while(getchar()!='\n')
    continue;

循环从输入中读取字符,包括按下Enter键产生的换行符。函数的返回值没有赋给ch,以上代买所做的只是读取并丢弃字符。由于最后丢弃的字符是换行符,所以下一个读取的字符是下一行的首字母。在外层的while循环中,getchar()读取的首字母并赋给ch。
假设用户一开始按Enter:

if(ch=='\n')
    continue;

7.7.3 多重标签

//  vowels.c    --使用多重标签
#include<stdio.h>
int main(void)
{
    char ch;
    int a_ct,e_ct,i_ct,o_ct,u_ct;

    a_ct=e_ct=i_ct=o_ct=u_ct=0;

    printf("Enter some text;enter # to quit.\n");
    while((ch=getchar())!='#')
    {
        switch(ch)
        {
            case 'a':
            case 'A':a_ct++;
                    break;
            case 'e':
            case 'E':e_ct++;
                    break;
            case 'i':
            case 'I':i_ct++;
                    break;
            case 'o':
            case 'O':o_ct++;
                    break;
            case 'u':
            case 'U':u_ct++;
                    break;
            default:break;
        }
    }
printf("number of vowels:A E I O U");
printf("%4d %4d %4d %4d %4d",a_ct,e_ct,i_ct,o_ct,u_ct);
return 0;
}

case 'u':后面的break可以去掉,但是防止再在后面添加东西时忘了break,因此还是保留较好。

//使用ctype库中的toupper()函数
while((ch=getchar())!='#')
{
    ch = toupper(ch);   //将字母转换为大写
    switch(ch)
    {
    case 'A':
    ...
    }
}

或者,也可以先不转换ch,把toupper(ch)放进switch的测试条件中;switch(toupper(ch)).

7.7.4 switch和if else

if else 在范围方面更好一点

7.8 goto语句

goto part2;
part2:printf("...");

7.8.1 避免使用goto

  • 处理包含多条语句的if语句:
    if(size>12)
    goto a;
    goto b;
    a: xxxxx;
    b: xxxxx;
    //在c中可以这样
    if(size>12)
    {
    xxxxx;
    }
    else 
    {
    xxxxx;
    }
  • 二选一
    C通过if else更清楚
  • 创建不确定循环
    redin:scanf("%d",&score);
    if(score<0)
    {
    goto stage2;
    }
    lots of statement
    goto readin;
    stage2:more stuff;

    用while代替

    scanf("%d",&score)
    while(score<=0)
    {
    lots of statement
    scanf("%d",&score);
    }
    more stuff;
  • 跳转到循环末尾,开始下一次迭代,C用continue代替
  • 跳出循环。C用break
  • 胡乱跳转至程序的不同部分。简而言之,不要这么做!
    可以接受:(出现问题时从一组嵌套循环中跳出,一条break只能跳出当前循环)

    while(funct > 0)
    {
    for(i=1;i<=100;i++)
    {
    for(j=1;j<=50;j++)
    {
        其他语句
        if(问题)
            goto help;
        其他语句
    }
    其他语句
    }
    其他语句
    }
    其他语句
    help:语句

    讽刺的是,虽然C根本不需要goto,但是它的goto比其他语言的goto好用,因为C允许在标签中使用描述性的单词而不是数字。

    // goto.c   --跳转测试
    #include<stdio.h>
    int main(void)
    {
    int a;
    int b;
    a1: scanf("%d %d",&a,&b);
    if(a==1)
    {
        goto a1;
    }
    goto a2;
        printf("hhahha");
    a2: printf("end");
    return 0;
    }

    7.9 关键概念

    智能的一个方面是,根据情况做出相应的响应。所以,选择语句是开发具有智能行为程序的基础。C语言通过if、if else和switch语句,以及条件运算符(?:)可以实现智能选择。


擦肩而过的概率