《C Primer Plus》第七章--C控制语句:分支和跳转(if else switch continue break goto 逻辑运算符ctype.h)

文章目录

加粗与下面的点是用来标记我的知识盲区,无特殊含义。

  • 它们通常是预处理宏,而不是真正的函数,第16章会讨论类似函数的宏
  • 熟悉C语言独特写法
  • ctype.h
  • if else 匹配
  • iso646.h
  • 优先级
  • 统计单词程序
  • 标签
  • goto

C控制语句:分支和跳转

本章内容

本章介绍以下内容:

  • ·关键字——if、else、switch、continue、break、case、default、goto;
  • ·运算符——&&、||、?: ;
  • ·函数——getchar()、putchar()、ctype.h系列;
  • ·如何使用if和if else语句,如何嵌套它们;
  • ·在更复杂的测试表达式中用逻辑运算符组合关系表达式;
  • ·C的条件运算符;
  • ·switch语句;
  • ·break、continue和goto语句;
  • ·使用C的字符I/O函数——getchar()和putchar();
  • ·ctype.h头文件提供的字符分析函数系列。

if语句

// 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: %.1f%% were below freezing.\n",
               all_days, 100.0 * (float)cold_days / all_days);
    if (all_days == 0)
        printf("No data entered!\n");
    return 0;
}

Enter the list of daily low temperatures.

Use Celsius, and enter q to quit.12 5 -2.5 0 6 8 -3 -10 5 10 q

10 days total: 30.0% were below freezing.

  • if语句指示计算机,如果刚读取的值(temperature)小于0,就把cold_days递增1;如果temperature不小于0,就跳过cold_days++;语句,while循环继续读取下一个温度值。

  • 接着,该程序又使用了两次if语句控制程序的输出。如果有数据,就打印结果;如果没有数据,就打印一条消息(稍后将介绍一种更好的方法来处理这种情况)。

  • 为避免整数除法,该程序示例把计算后的百分比强制转换为float类型。其实,也不必使用强制类型转换,因为在表达式100.0 * cold_days / all_days中,将首先对表达式100.0 * cold_days求值,由于C的自动转换类型规则,乘积会被强制转换成浮点数。但是,使用强制类型转换可以明确表达转换类型的意图,保护程序免受不同版本编译器的影响。

  • if语句被称为分支语句(branching statement)或选择语句(selection statement),因为它相当于一个交叉点,程序要在两条分支中选择一条执行。

  • if ( expression ) statement

  • 如果对expression求值为真(非0),则执行statement;否则,跳过statement。与while循环一样,statement可以是一条简单语句或复合语句。if语句的结构和while语句很相似,它们的主要区别是:如果满足条件可执行的话,if语句只能测试和执行一次,而while语句可以测试和执行多次。

if (score > big)

printf(“Jackpot!\n”); // 简单语句

if (joe > ron)

{ // 复合语句

joecash++;

printf(“You lose, Ron.\n”);

}

if else语句

C还提供了if else形式,可以在两条语句之间作选择。

if (all_days != 0)
    printf("%d days total: %.1f%% were below freezing.\n",
           all_days, 100.0 * (float)cold_days / all_days);
else
    printf("No data entered!\n");
  • 如果if语句的测试表达式为真,就打印温度数据;如果为假,就打印警告消息。
if ( expression )
    statement1
else
    statement2
  • 如果对expression求值为真(非0),则执行statement;否则,跳过statement。与while循环一样,statement可以是一条简单语句或复合语句。if语句的结构和while语句很相似,它们的主要区别是:如果满足条件可执行的话,if语句只能测试和执行一次,而while语句可以测试和执行多次。
  • 如果要在if和else之间执行多条语句,必须用花括号把这些语句括起来成为一个块。下面的代码结构违反了C语法,因为在if和else之间只允许有一条语句(简单语句或复合语句):
if (x > 0)
    printf("Incrementing x:\n");
    x++;
else    // 将产生一个错误
    printf("x <= 0 \n");
  • 编译器把printf()语句视为if语句的一部分,而把x++;看作一条单独的语句,它不是if语句的一部分。然后,编译器发现else并没有所属的if,这是错误的。上面的代码应该这样写:
if (x > 0)
{
    printf("Incrementing x:\n");
    x++;
}
else
    printf("x <= 0 \n");
  • if语句用于选择是否执行一个行为,而if else语句用于在两个行为之间选择。

《C Primer Plus》第七章--C控制语句:分支和跳转(if else switch continue break goto 逻辑运算符ctype.h)

另一个示例:介绍getchar()和putchar()

  • getchar()函数不带任何参数,它从输入队列中返回下一个字符。例如,下面的语句读取下一个字符输入,并把该字符的值赋给变量ch:

    ch = getchar();

  • 该语句与下面的语句效果相同:

    scanf("%c", &ch);

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

    putchar(ch);

  • 该语句与下面的语句效果相同:

    printf("%c", ch);

  • 由于这些函数只处理字符,所以它们比更通用的scanf()和printf()函数更快、更简洁。

  • 它们通常是预处理宏,而不是真正的函数,第16章会讨论类似函数的宏

  • “如果字符是空白,原样打印;否则,打印原字符在ASCII序列中的下一个字符”

// cypher1.c -- 更改输入,空格不变
#include <stdio.h>
#define SPACE ' '
// 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;
}
  • 替换成下面形式的循环:

    while ((ch = getchar()) != ‘\n’)

    {

    /* 处理字符 */

    }

  • 以上执行的行为是赋值给ch和把ch的值与换行符作比较。表达式ch = getchar()两侧的圆括号使之成为!=运算符的左侧运算对象。要对该表达式求值,必须先调用getchar()函数,然后把该函数的返回值赋给ch。因为赋值表达式的值是赋值运算符左侧运算对象的值,所以ch = getchar()的值就是ch的新值,因此,读取ch的值后,测试条件相当于是ch != ‘\n’(即,ch不是换行符)。

  • 这种独特的写法在C编程中很常见,应该多熟悉它。还要记住合理使用圆括号组合子表达式。 上面例子中的圆括号都必不可少。假设省略ch = getchar()两侧的圆括号:

    while (ch = getchar() != ‘\n’)

  • !=运算符的优先级比=高,所以先对表达式getchar() != '\n’求值。由于这是关系表达式,所以其值不是1就是0(真或假)。然后,把该值赋给ch。省略圆括号意味着赋给ch的值是0或1,而不是getchar()的返回值。这不是我们的初衷。

ctype.h系列的字符函数

  • C有一系列专门处理字符的函数,ctype.h头文件包含了这些函数的原型。这些函数接受一个字符作为参数,如果该字符属于某特殊的类别,就返回一个非零值(真);否则,返回0(假)。
  • 例如,如果isalpha()函数的参数是一个字母,则返回一个非零值。
// cypher2.c -- 替换输入的字母,非字母字符保持不变
#include <stdio.h>
#include <ctype.h> // 包含isalpha()的函数原型
int main(void)
{
    char ch;
    while ((ch = getchar()) != '\n')
    {
        if (isalpha(ch))     // 如果是一个字符,
            putchar(ch + 1); // 显示该字符的下一个字符
        else                 // 否则,
            putchar(ch);     // 原样显示
    }
    putchar(ch); // 显示换行符
    return 0;
}
  • 字符映射函数不会修改原始的参数,这些函数只会返回已修改的值。也就是说,下面的语句不改变ch的值:

    tolower(ch); // 不影响ch的值

  • 这样做才会改变ch的值:

    ch = tolower(ch);

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

  • 《C Primer Plus》第七章--C控制语句:分支和跳转(if else switch continue break goto 逻辑运算符ctype.h)

  • ctype.h头文件中的字符映射函数

  • 《C Primer Plus》第七章--C控制语句:分支和跳转(if else switch continue break goto 逻辑运算符ctype.h)

多重选择else if

  • 电力公司通常根据客户的总用电量来决定电费。下面是某电力公司的电费清单,单位是千瓦时(kWh):

    首 360kWh: $0.13230/kWh

    续 108kWh: $0.15040/kWh

    续 252kWh: $0.30025/kWh

    超过 720kWh: $0.34025/kWh

// electric.c -- 计算电费
#include <stdio.h>
#define RATE1 0.13230                               // 首次使用 360 kwh 的费率
#define RATE2 0.15040                               // 接着再使用 108 kwh 的费率
#define RATE3 0.30025                               // 接着再使用 252 kwh 的费率
#define RATE4 0.34025                               // 使用超过 720kwh 的费率
#define BREAK1 360.0                                // 费率的第1个分界点
#define BREAK2 468.0                                // 费率的第2个分界点
#define BREAK3 720.0                                // 费率的第3个分界点
#define BASE1 (RATE1 * BREAK1)                      // 使用360kwh的费用
#define BASE2 (BASE1 + (RATE2 * (BREAK2 - BREAK1))) // 使用468kwh的费用
#define BASE3 (BASE2 + (RATE3 * (BREAK3 - BREAK2))) // 使用720kwh的费用
int main(void)
{
    double kwh;  // 使用的千瓦时
    double bill; // 电费
    printf("Please enter the kwh used.\n");

    scanf("%lf", &kwh); // %lf对应double类型
    if (kwh <= BREAK1)

        bill = RATE1 * kwh;
    else if (kwh <= BREAK2) // 360~468 kwh
        bill = BASE1 + (RATE2 * (kwh - BREAK1));
    else if (kwh <= BREAK3) // 468~720 kwh
        bill = BASE2 + (RATE3 * (kwh - BREAK2));
    else // 超过 720 kwh
        bill = BASE3 + (RATE4 * (kwh - BREAK3));
    printf("The charge for %.1f kwh is $%1.2f.\n", kwh, bill);
    return 0;
}
  • 程序清单7.4用符号常量表示不同的费率和费率分界点,以便把常量统一放在一处。这样,电力公司在更改费率以及费率分界点时,更新数据非常方便。BASE1和BASE2根据费率和费率分界点来表示。一旦费率或分界点发生了变化,它们也会自动更新。预处理器是不进行计算的。程序中出现BASE1的地方都会被替换成0.13230*360.0。不用担心,编译器会对该表达式求值得到一个数值(47.628),以便最终的程序代码使用的是47.628而不是一个计算式。

  • 该程序根据kwh的值在3个公式之间选择一个。特别要注意的是,如果kwh大于360,程序只会到达第1个else。因此,else if (kwh <= BREAK2)这行相当于要求kwh在360~482之间,如程序注释所示。类似地,只有当kwh的值超过720时,才会执行最后的else。最后,注意BASE1、BASE2和BASE3分别代表360、468和720千瓦时的总费用。因此,当电量超过这些值时,只需要加上额外的费用即可。

  • 对于编译器的限制范围,C99标准要求编译器最少支持127层套嵌。

else与if配对

if (number > 6)

if (number < 12)

​ printf(“You’re close!\n”);

else

printf(“Sorry, you lose a turn!\n”);

  • 何时打印Sorry, you lose a turn!?当number小于或等于6时,还是number大于12时?换言之,else与第1个if还是第2个if匹配?答案是,else与第2个if匹配。也就是说,输入的数字和匹配的响应如下:

    数字 响应

    5 None

    10 You’re close!

    15 Sorry, you lose a turn!

  • 规则是,如果没有花括号,else与离它最近的if匹配,除非最近的if被花括号括起来

if (number > 6)
{
    if (number < 12)
        printf("You're close!\n");
}
else
    printf("Sorry, you lose a turn!\n");
这样改动后,响应如下:
数字 响应 
5 Sorry, you lose a turn !
10 You’re close !
15 None

多层嵌套的if语句

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

  • 伪代码:
    提示用户输入数字
    当scanf()返回值为1
    分析该数并报告结果
    提示用户继续输入

  • 下一步,设计如何找出约数。也许最直接的方法是:

for (div = 2; div < num; div++)
    if (num % div == 0)
        printf("%d is divisible by %d\n", num, div);
  • 该循环检查2~num之间的所有数字,测试它们是否能被num整除。但是,这个方法有点浪费时间。我们可以改进一下。例如,考虑如果144%2得0,说明2是144的约数;如果144除以2得72,那么72也是144的一个约数。所以,num % div测试成功可以获得两个约数。为了弄清其中的原理,我们分析一下循环中得到的成对约数:2和72、3和48、4和36、6和24、8和18、9和16、12和12、16和9、18和8,等等。在得到12和12这对约数后,又开始得到已找到的相同约数(次序相反)。因此,不用循环到143,在达到12以后就可以停止循环。这大大地节省了循环时间!

  • 分析后发现,必须测试的数只要到num的平方根就可以了,不用到num。对于9这样的数字,不会节约很多时间,但是对于10000这样的数,使用哪一种方法求约数差别很大。

for (div = 2; (div * div) <= num; div++)
    if (num % div == 0)
        printf("%d is divisible by %d and %d.\n",num, div, num / div);
  • **不使用平方根而用这样的测试条件,有两个原因。其一,整数乘法比求平方根快。**其二,我们还没有正式介绍平方根函数。
  • 如果num是素数,程序流不会进入if语句。要解决这个问题,可以在外层循环把一个变量设置为某个值(如,1),然后在if语句中把该变量重新设置为0。循环完成后,检查该变量是否是1,如果是,说明没有进入if语句,那么该数就是素数。这样的变量通常称为标记(flag)。
// 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 another integer for analysis; ");

        printf("Enter q to quit.\n");
    }
    printf("Bye.\n");
    return 0;
}
  • 注意,该程序在for循环的测试表达式中使用了逗号运算符,这样每次输入新值时都可以把isPrime设置为true。

逻辑运算符

  • 例如,要编写一个程序,计算输入的一行句子中除单引号和双引号以外其他字符的数量。这种情况下可以使用逻辑运算符,并使用句点(.)标识句子的末尾。
// 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;
}
  • if语句的测试条件中使用了逻辑与运算符&&。该if语句翻译成文字是“如果待测试的字符不是双引号,并且它也不是单引号,那么charcount递增1”。

  • 逻辑运算符两侧的条件必须都为真,整个表达式才为真。逻辑运算符的优先级比关系运算符低,所以不必在子表达式两侧加圆括号。

  • C有3种逻辑运算符

    • ·当且仅当exp1和exp2都为真时,exp1 && exp2才为真;
    • ·如果exp1或exp2为真,则exp1 || exp2为真;
    • ·如果exp1为假,则!exp1为真;如果exp1为真,则!exp1为假。
  • 下面是一些具体的例子:

    5 > 2 && 4 > 7为假,因为只有一个子表达式为真;

    5 > 2 || 4 > 7为真,因为有一个子表达式为真;

    !(4 > 7)为真,因为4不大于7。

备选拼写:iso646.h头文件

  • C是在美国用标准美式键盘开发的语言。但是在世界各地,并非所有的键盘都有和美式键盘一样的符号。因此,C99标准新增了可代替逻辑运算符的拼写,它们被定义在iso646.h头文件中。如果在程序中包含该头文件,便可用and代替&&、or代替||、not代替!。

优先级

  • !运算符的优先级很高,比乘法运算符还高,与递增运算符的优先级相同,只比圆括号的优先级低。&&运算符的优先级比||运算符高,但是两者的优先级都比关系运算符低,比赋值运算符高。因此,表达式a > b && b > c || b > d相当于((a > b) && (b > c)) || (b > d)。
  • 也就是说,b介于a和c之间,或者b大于d。
  • 尽管对于该例没必要使用圆括号,但是许多程序员更喜欢使用带圆括号的第2种写法。这样做即使不记得逻辑运算符的优先级,表达式的含义也很清楚。

求值顺序

  • 除了两个运算符共享一个运算对象的情况外,C通常不保证先对复杂表达式中哪部分求值。例如,下面的语句,可能先对表达式5+3求值,也可能先对表达式9+6求值:
    apples = (5 + 3) * (9 + 6);
  • 但是,对于逻辑运算符是个例外,C保证逻辑表达式的求值顺序是从左往右。&&和||运算符都是序列点,所以程序在从一个运算对象执行到下一个运算对象之前,所有的副作用都会生效。而且,C保证一旦发现某个元素让整个表达式无效,便立即停止求值。

范围

  • 例如,要测试score是否在90~100的范围内,可以这样写:

    if (range >= 90 && range <= 100)

    printf(“Good show!\n”);

  • 千万不要模仿数学上的写法:

    if (90 <= range <= 100) // 千万不要这样写!

    printf(“Good show!\n”);

  • 这样写的问题是代码有语义错误,而不是语法错误,所以编译器不会捕获这样的问题(虽然可能会给出警告)。由于<=运算符的求值顺序是从左往右,所以编译器把测试表达式解释为:

    (90 <= range) <= 100

  • 子表达式90 <= range的值要么是1(为真),要么是0(为假)。这两个值都小于100,所以不管range的值是多少,整个表达式都恒为真。因此,在范围测试中要使用&&。

一个统计单词的程序

  • 现在,我们可以编写一个统计单词数量的程序(即,该程序读取并报告单词的数量)。该程序还可以计算字符数和行数。

  • 伪代码如下:

    • 读取一个字符
    • 当有更多输入时
    • 递增字符计数
    • 如果读完一行,递增行数计数
    • 如果读完一个单词,递增单词计数
    • 读取下一个字符
// wordcnt.c -- 统计字符数、单词数、行数
#include <stdio.h>
#include <ctype.h>   // 为isspace()函数提供原型
#include <stdbool.h> // 为bool、true、false提供定义
#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 = %ld, words = %d, lines = %d, ", n_chars, n_words, n_lines);

    printf("partial lines = %d\n", p_lines);
    return 0;
}
  • “如果c不是空白字符,且如果c不在单词里”。如果两个条件都满足,则一定是一个新单词的开头,所以要递增n_words。如果位于单词中,满足第1个条件,但是inword为true,就不递增n_word。当读到下一个空白字符时,inword被再次设置为false。检查代码,查看一下如果单词之间有多个空格时,程序是否能正常运行。

处理两个单词多个空格的情况

条件运算符:?:

  • 该运算符分为两部分,需要3个运算对象。回忆一下,带一个运算对象的运算符称为一元运算符,带两个运算对象的运算符称为二元运算符。以此类推,带3个运算对象的运算符称为三元运算符。

  • 条件运算符是C语言中唯一的三元运算符。下面的代码得到一个数的绝对值:

    x = (y < 0) ? -y : y;

  • 在=和;之间的内容就是条件表达式,该语句的意思是“如果y小于0,那么x = -y;否则,x = y”。

  • 用if else可以这样表达:

    if (y < 0)

    x = -y;

    else

    x = y;

    expression1 ? expression2 : expression3

  • 如果expression1为真(非0),那么整个条件表达式的值与expression2的值相同;如果expression1为假(0),那么整个条件表达式的值与expression3的值相同。

  • 该程序计算刷给定平方英尺的面积需要多少罐油漆。基本算法很简单:用平方英尺数除以每罐油漆能刷的面积。但是,商店只卖整罐油漆,不会拆分来卖,所以如果计算结果是1.7罐,就需要两罐。因此,该程序计算得到带小数的结果时应该进1。条件运算符常用于处理这种情况,而且还要根据单复数分别打印can和cans。

/* 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;
}
  • 该程序使用的变量都是int类型,除法的计算结果(sq_feet / COVERAGE)会被截断。也就是说,351/350得1。所以,cans被截断成整数部分。如果sq_feet % COVERAGE得0,说明sq_feet被COVERAGE整除,cans的值不变;否则,肯定有余数,就要给cans加1。这由下面的语句完成:

    cans += ((sq_feet % COVERAGE == 0)) ? 0 : 1;

  • 该语句把+=右侧表达式的值加上cans,再赋给cans。右侧表达式是一个条件表达式,根据sq_feet是否能被COVERAGE整除,其值为0或1。

  • printf()函数中的参数也是一个条件表达式:

    cans == 1 ? “can” : “cans”);

  • 如果cans的值是1,则打印can;否则,打印cans。

循环辅助:continue和break

continue语句

  • 3种循环都可以使用continue语句。执行到该语句时,会跳过本次迭代的剩余部分,并开始下一轮迭代。如果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 is %0.1f.\n", n, total / n);
        printf("Low = %0.1f, high = %0.1f\n", min, max);
    }
    else
        printf("No valid scores were entered.\n");
    return 0;
}
  • while循环读取输入,直至用户输入非数值数据。循环中的if语句筛选出无效的分数。假设输入188,程序会报告:188 is an invalid value。在本例中,continue语句让程序跳过处理有效输入部分的代码。程序开始下一轮循环,准备读取下一个输入值。

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

    while (getchar() != ‘\n’)

    ;

  • 当程序已经读取一行中的某些内容,要跳至下一行开始处时,这种用法很方便。问题是,一般很难注意到一个单独的分号。如果使用continue,可读性会更高:

    while (getchar() != ‘\n’)

    continue;

  • 如果用了continue没有简化代码反而让代码更复杂,就不要使用continue。例如,考虑下面的程序段:

    while ((ch = getchar() ) != ‘\n’)

    {

    if (ch == ‘\t’)

    ​ continue;

    putchar(ch);

    }

  • 该循环跳过制表符,并在读到换行符时退出循环。以上代码这样表示更简洁:

    while ((ch = getchar()) != ‘\n’)

    if (ch != ‘\t’)

    ​ putchar(ch);

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

  • 对于while和do while循环,执行continue语句后的下一个行为是对循环的测试表达式求值。

  • 对于for循环,执行continue后的下一个行为是对更新表达式求值,然后是对循环测试表达式求值。

break语句

  • 程序执行到循环中的break语句时,会终止包含它的循环,并继续执行下一阶段。

  • 如果break语句位于嵌套循环内,它只会影响包含它的当前循环。

  • 《C Primer Plus》第七章--C控制语句:分支和跳转(if else switch continue break goto 逻辑运算符ctype.h)

  • break还可用于因其他原因退出循环的情况。程序清单7.10用一个循环计算矩形的面积。如果用户输入非数字作为矩形的长或宽,则终止循环。

/* break.c -- 使用 break 退出循环 */
#include <stdio.h>
int main(void)
{
    float length, width;
    printf("Enter 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("Enter the length of the rectangle:\n");
    }
    printf("Done.\n");
    return 0;
}
  • 和continue一样,如果用了break代码反而更复杂,就不要使用break。例如,考虑下面的循环:

    while ((ch = getchar()) != ‘\n’)

    {

    if (ch == ‘\t’)

    ​ break;

    putchar(ch);

    }

  • 如果把两个测试条件放在一起,逻辑就更清晰了:

    while ((ch = getchar() ) != ‘\n’ && ch != ‘\t’)

    putchar(ch);

  • 在for循环中的break和continue的情况不同,执行完break语句后会直接执行循环后面的第1条语句,连更新部分也跳过。嵌套循环内层的break只会让程序跳出包含它的当前循环,要跳出外层循环还需要一个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);
}

多重选择: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 ");
    printf("an animal name\nbeginning 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("argali, a wild sheep of Asia\n");
                break;
            case 'b':
                printf("babirusa, a wild pig of Malay\n");
                break;
            case 'c':
                printf("coati, racoonlike mammal\n");
                break;
            case 'd':
                printf("desman, aquatic, molelike critter\n");
                break;
            case 'e':
                printf("echidna, the spiny anteater\n");
                break;
            case 'f':
                printf("fisher, brownish marten\n");
                break;
            default:
                printf("That's a stumper!\n");

            } /* switch结束      */
        else
            printf("I recognize only lowercase letters.\n");
        while (getchar() != '\n')
            continue; /* 跳过输入行的剩余部分  */
        printf("Please type another letter or a #.\n");

    } /* while循环结束    */
    printf("Bye!\n");
    return 0;
}

Give me a letter of the alphabet, and I will give an animal name

beginning with that letter.

Please type in a letter; type # to end my act.a [enter]

argali, a wild sheep of Asia

Please type another letter or a #.dab [enter]

desman, aquatic, molelike critter

Please type another letter or a #.r [enter]

That’s a stumper!

Please type another letter or a #.Q [enter]

I recognize only lowercase letters.

Please type another letter or a #.# [enter]

Bye!

switch语句

  • 要对紧跟在关键字switch后圆括号中的表达式求值。在程序清单7.11中,该表达式是刚输入给ch的值。然后程序扫描标签(这里指,case ‘a’ :、case ‘b’ :等)列表,直到发现一个匹配的值为止。然后程序跳转至那一行。如果没有匹配的标签怎么办?如果有default :标签行,就跳转至该行;否则,程序继续执行在switch后面的语句。

  • break语句在其中起什么作用?它让程序离开switch语句,跳至switch语句后面的下一条语句。如果没有break语句,就会从匹配标签开始执行到switch末尾。

  • 例如,如果删除该程序中的所有break语句,运行程序后输入d,其交互的输出结果如下:

Give me a letter of the alphabet, and I will give an animal name

beginning with that letter.

Please type in a letter; type # to end my act.d [enter]

desman, aquatic, molelike critter

echidna, the spiny anteater

fisher, a brownish marten

That’s a stumper!

Please type another letter or a #.# [enter]

Bye!

  • break语句可用于循环和switch语句中,但是continue只能用于循环中。

  • 尽管如此,如果switch语句在一个循环中,continue便可作为switch语句的一部分。这种情况下,就像在其他循环中一样,continue让程序跳出循环的剩余部分,包括switch语句的其他部分。

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

switch ( 整型表达式)

{

case 常量1:

​ 语句 <–可选

case 常量2:

​ 语句 <–可选

default : <–可选

​ 语句 <–可选

}

只读每行的首字符

  • animals.c的另一个独特之处是它读取输入的方式。运行程序时读者可能注意到了,当输入dab时,只处理了第1个字符。这种丢弃一行中其他字符的行为,经常出现在响应单字符的交互程序中。

多重标签

// 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;

        } // switch结束
    }     // while循环结束
    printf("number of vowels:  A  E  I  O  U\n");
    printf("         %4d %4d %4d %4d %4d\n", a_ct, e_ct, i_ct, o_ct, u_ct);
    return 0;
}
  • 假设如果ch是字母i,switch语句会定位到标签为case ‘i’ :的位置。由于该标签没有关联break语句,所以程序流直接执行下一条语句,即i_ct++;。如果ch是字母I,程序流会直接定位到case ‘I’ :。本质上,两个标签都指的是相同的语句。

Enter some text; enter # to quit.I see under the overseer.#

number of vowels: A E I O U

​ 0 7 1 1 1

  • 在该例中,如果使用ctype.h系列的toupper()函数(参见表7.2)可以避免使用多重标签,在进行测试之前就把字母转换成大写字母:
while ((ch = getchar()) != '#')

{
    ch = toupper(ch);
    switch (ch)
    {
    case 'A':
        a_ct++;
        break;
    case 'E':
        e_ct++;
        break;
    case 'I':
        i_ct++;
        break;
    case 'O':
        o_ct++;
        break;
    case 'U':
        u_ct++;
        break;
    default:
        break;

    } // switch结束
} // while循环结束

switch和if else

  • 你经常会别无选择。如果是根据浮点类型的变量或表达式来选择,就无法使用switch。如果根据变量在某范围内决定程序流的去向,使用switch就很麻烦,这种情况用if就很方便:

    if (integer < 1000 && integer > 2)

  • 使用switch要涵盖以上范围,需要为每个整数(3~999)设置case标签。但是,如果使用switch,程序通常运行快一些,生成的代码少一些。

goto语句

  • Kernighan和Ritchie提到goto语句“易被滥用”,并建议“谨慎使用,或者根本不用”。

  • goto语句有两部分:goto和标签名。标签的命名遵循变量命名规则,如下所示:
    goto part2;

  • 要让这条语句正常工作,函数还必须包含另一条标为part2的语句,该语句以标签名后紧跟一个冒号开始:

    part2: printf(“Refined analysis:\n”);

避免使用goto

  • 我们先概述一些使用goto的常见情况,然后再介绍C的解决方案。

·处理包含多条语句的if语句:

if (size > 12)

goto a;

goto b;

a: cost = cost * 1.05;

flag = 2;

b: bill = cost * flag;

  • 对于以前的BASIC和FORTRAN,只有直接跟在if条件后面的一条语句才属于if,不能使用块或复合语句。我们把以上模式转换成等价的C代码,标准C用复合语句或块来处理这种情况:

    if (size > 12)

    {

    cost = cost * 1.05;

    flag = 2;

    }

    bill = cost * flag;

  • 二选一:

    if (ibex > 14)

    goto a;

    sheds = 2;

    goto b;

    a: sheds= 3;

    b: help = 2 * sheds;

  • C通过if else表达二选一更清楚:

    if (ibex > 14)

    sheds = 3;

    else

    sheds = 2;

    help = 2 * sheds;

  • 创建不确定循环:

    readin: scanf("%d", &score);

    if (score < O)

    goto stage2;

    lots of statements

    goto readin;

    stage2: more stuff;

  • C用while循环代替:

    scanf("%d", &score);

    while (score >= 0)

    {

    lots of statements

    scanf("%d", &score);

    }

    more stuff;

  • 跳转至循环末尾,并开始下一轮迭代。C使用continue语句代替。

  • 跳出循环。C使用break语句。实际上,break和continue是goto的特殊形式。使用break和continue的好处是:其名称已经表明它们的用法,而且这些语句不使用标签,所以不用担心把标签放错位置导致的危险。

  • 胡乱跳转至程序的不同部分。简而言之,不要这样做!

  • 但是,C程序员可以接受一种goto的用法——出现问题时从一组嵌套循环中跳出(一条break语句只能跳出当前循环):

while (funct > 0)
{
    for (i = 1; i <= 100; i++)
    {
        for (j = 1; j <= 50; j++)
        {
            其他语句 if (问题) goto help;
            其他语句
        }
        其他语句
    }
    其他语句
}
其他语句
help : 语句
  • goto语句使程序控制跳转至相应标签语句。冒号用于分隔标签和标签语句。标签名遵循变量命名规则。标签语句可以出现在goto的前面或后面。

关键概念

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

  • if和if else语句使用测试条件来判断执行哪些语句。所有非零值都被视为true,零被视为false。测试通常涉及关系表达式(比较两个值)、逻辑表达式(用逻辑运算符组合或更改其他表达式)。

  • 要记住一个通用原则,如果要测试两个条件,应该使用逻辑运算符把两个完整的测试表达式组合起来。例如,下面这些是错误的:

  • if (a < x < z) // 错误,没有使用逻辑运算符

  • …
    if (ch != ‘q’ && != ‘Q’) // 错误,缺少完整的测试表达式

  • 正确的方式是用逻辑运算符连接两个关系表达式:

  • if (a < x && x < z) // 使用&&组合两个表达式


  • if (ch != ‘q’ && ch != ‘Q’) // 使用&&组合两个表达式

  • 对比这两章和前几章的程序示例可以发现:使用第6章、第7章介绍的语句,可以写出功能更强大、更有趣的程序。

本章小结

  • 本章介绍了很多内容,我们来总结一下。if语句使用测试条件控制程序是否执行测试条件后面的一条简单语句或复合语句。如果测试表达式的值是非零值,则执行语句;如果测试表达式的值是零,则不执行语句。if else语句可用于二选一的情况。如果测试条件是非零,则执行else前面的语句;如果测试表达式的值是零,则执行else后面的语句。在else后面使用另一个if语句形成else if,可构造多选一的结构。
    测试条件通常都是关系表达式,即用一个关系运算符(如,<或==)的表达式。使用C的逻辑运算符,可以把关系表达式组合成更复杂的测试条件。
  • 在多数情况下,用条件运算符(?:)写成的表达式比if else语句更简洁。
  • ctype.h系列的字符函数(如,isspace()和isalpha())为创建以分类字符为基础的测试表达式提供了便捷的工具。
  • switch语句可以在一系列以整数作为标签的语句中进行选择。如果紧跟在switch关键字后的测试条件的整数值与某标签匹配,程序就转至执行匹配的标签语句,然后在遇到break之前,继续执行标签语句后面的语句。
  • break、continue和goto语句都是跳转语句,使程序流跳转至程序的另一处。break语句使程序跳转至紧跟在包含break语句的循环或switch末尾的下一条语句。continue语句使程序跳出当前循环的剩余部分,并开始下一轮迭代。

复习题

  1. 判断下列表达式是true还是false。

a.100 > 3 && ‘a’>‘c’

b.100 > 3 || ‘a’>‘c’

c.!(100>3)

  1. 根据下列描述的条件,分别构造一个表达式:

a.number等于或大于90,但是小于100

b.ch不是字符q或k

c.number在1~9之间(包括1和9),但不是5

d.number不在1~9之间

  1. 下面的程序关系表达式过于复杂,而且还有些错误,请简化并改正。
#include <stdio.h>
int main(void)                                                     /* 1 */
{                                                                  /* 2 */
    int weight, height; /* weight以磅为单位,height以英寸为单位 */   /* 4 */
    scanf("%d", weight, height);                                   /* 5 */
    if (weight < 100 && height > 64)                               /* 6 */
        if (height >= 72)                                          /* 7 */
            printf("You are very tall for your weight.\n");
        else if (height<72 &&> 64)                     /* 9 */
            printf("You are tall for your weight.\n"); /* 10 */
        else if (weight > 300 && !(weight <= 300)      /* 11 */
                 && height < 48)                       /* 12 */
            if (!(height >= 48))                       /* 13 */
                printf(" You are quite short for your weight.\n");
            else                                   /* 15 */
                printf("Your weight is ideal.\n"); /* 16 */
    return 0;                                      /* 17 */
}
  1. 下列各表达式的值是多少?

a.5 > 2

b.3+4 > 2 && 3 < 2

c.x >= y || y > x

d.d = 5+( 6 > 2 )

e.‘X’ > ‘T’ ? 10 : 5

f. x > y ? y > x : x > y

  1. 下面的程序将打印什么?

    #include <stdio.h>
    int main(void)
    {
        int num;
        for (num = 1; num <= 11; num++)
        {
            if (num % 3 == 0)
                putchar('$');
            else
                putchar('*');
            putchar('#');
            putchar('%');
        }
        putchar('\n');
        return 0;
    }
    
  2. 下面的程序将打印什么?

    #include <stdio.h>
    int main(void)
    {
        int i = 0;
        while (i < 3)
        {
            switch (i++)
            {
            case 0:
                printf("fat ");
            case 1:
                printf("hat ");
            case 2:
                printf("cat ");
            default:
                printf("Oh no!");
            }
            putchar('\n');
        }
        return 0;
    }
    
  3. 下面的程序有哪些错误?

    #include <stdio.h>
    int main(void)
    {
        char ch;
        int lc = 0;
        /* 统计小写字母
        int uc = 0; /* 统计大写字母
        int oc = 0; /* 统计其他字母
        while ((ch = getchar()) != '#')
        {    
            if ('a' <= ch >= 'z')  
                 lc++;   
            else if (!(ch < 'A') || !(ch > 'Z')
                 uc++;   
                oc++;  
        } 
        printf(%d lowercase, %d uppercase, %d other, lc, uc, oc);
        return 0;
     }
    
  4. 下面的程序将打印什么?

    /* retire.c */
    #include <stdio.h>
    int main(void)
    {
        int age = 20;
        while (age++ <= 65)
        {
            if ((age % 20) == 0) /* age是否能被20整除? */
                printf("You are %d. Here is a raise.\n", age);
            if (age = 65)
                printf("You are %d. Here is your gold watch.\n", age);
        }
        return 0;
    }
    
  5. 给定下面的输入时,以下程序将打印什么?

    q
    c
    h
    b
    #include <stdio.h>
        int
        main(void)
    {
        char ch;
        while ((ch = getchar()) != '#')
        {
            if (ch == '\n')
                continue;
            printf("Step 1\n");
            if (ch == 'c')
                continue;
            else if (ch == 'b')
                break;
            else if (ch == 'h')
                goto laststep;
            printf("Step 2\n");
        laststep:
            printf("Step 3\n");
        }
        printf("Done\n");
        return 0;
    }
    
  6. 重写复习题9,但这次不能使用continue和goto语句。


  1. b是true。

  2. a.number >= 90 && number < 100
    b.ch != ‘q’ && ch != ‘k’
    c.(number >= 1 && number <= 9) && number != 5
    d.可以写成!(number >= 1 && number <= 9),但是number < 1 || number > 9更好理解。

    • 第5行:应该是scanf("%d %d", &weight, &height);。不要忘记scanf()中要用&。另外,这一行前面应该有提示用户输入的语句。
    • 第9行:测试条件中要表达的意思是(height < 72 && height > 64)。根据前面第7行中的测试条件,能到第9行的height一定小于72,所以,只需要用表达式(height > 64)即可。但是,第6行中已经包含了height > 64这个条件,所以这里完全不必再判断,if else应改成else。
    • 第11行:条件冗余。第2个表达式(weight不小于或不等于300)和第1个表达式含义相同。只需用一个简单的表达式(weight > 300)即可。但是,问题不止于此。第11行是一个错误的if,这行的else if与第6行的if匹配。但是,根据if的“最接近规则”,该else if应该与第9行的else if匹配。因此,在weight小于100且小于或等于64时到达第11行,而此时weight不可能超过300。
      第7行~第10行:应该用花括号括起来。这样第11行就确定与第6行匹配。但是,如果把第9行的else if替换成简单的else,就不需要使用花括号。
    • 第13行:应简化成if (height > 48)。实际上,完全可以省略这一行。因为第12行已经测试过该条件。
    • 下面是修改后的版本:
    #include <stdio.h>
    int main(void)
    {
        int weight, height; /* weight in lbs, height in inches */
        printf("Enter your weight in pounds and ");
        printf("your height in inches.\n");
        scanf("%d %d", &weight, &height);
        if (weight < 100 && height > 64)
            if (height >= 72)
                printf("You are very tall for your weight.\n");
            else
                printf("You are tall for your weight.\n");
        else if (weight > 300 && height < 48)
            printf(" You are quite short for your weight.\n");
        else
            printf("Your weight is ideal.\n");
        return 0;
    }
    
  3. a.1。5确实大于2,表达式为真,即是1。
    b.0。3比2大,表达式为假,即是0。
    c.1。如果第1个表达式为假,则第2个表达式为真,反之亦然。所以,只要一个表达式为真,整个表达式的结果即为真。
    d.6。因为6 > 2为真,所以(6 > 2)的值为1。
    e.10。因为测试条件为真。
    f.0。如果x > y为真,表达式的值就是y > x,这种情况下它为假或0。如果x > y为假,那么表达式的值就是x > y,这种情况下为假。

  4. 该程序打印以下内容:

    *#%*#%$#%*#%*#%​$#%*#%*#%$#%*#%*#%
    

    无论怎样缩排,每次循环都会打印#,因为缩排并不能让putchar(’#’);成为if else复合语句的一部分。

  5. 程序打印以下内容:

    fat hat cat Oh no!

    hat cat Oh no!

    cat Oh no!

    • 第5行~第7行的注释要以*/结尾,或者把注释开头的/*换成//。表达式’a’ <= ch >= 'z’应替换成ch >= ‘a’ && ch <= ‘z’。
    • 或者,包含ctype.h并使用islower(),这种方法更简单,而且可移植性更高。顺带一提,虽然从C的语法方面看,‘a’ <= ch >= 'z’是有效的表达式,但是它的含义不明。因为关系运算符从左往右结合,该表达式被解释成(‘a’ <= ch) >= ‘z’。圆括号中的表达式的值不是1就是0(真或假),然后判断该值是否大于或等于’z’的数值码。1和0都不满足测试条件,所以整个表达式恒为0(假)。在第2个测试表达式中,应该把||改成&&。另外,虽然!(ch< ‘A’)是有效的表达式,而且含义也正确,但是用ch >= 'A’更简单。这一行的’z’后面应该有两个圆括号。更简单的方法是使用isuupper()。在uc++;前面应该加一行else。否则,每输入一个字符,uc都会递增1。另外,在printf()语句中的格式化字符串应该用双引号括起来。下面是修改后的版本:
    #include <stdio.h>
    #include <ctype.h>
    int main(void)
    {
        char ch;
        int lc = 0; /*统计小写字母*/
        int uc = 0; /*统计大写字母*/
        int oc = 0; /*统计其他字母*/
        while ((ch = getchar()) != '#')
        {
            if (islower(ch))
                lc++;
            else if (isupper(ch))
                uc++;
            else
                oc++;
        }
        printf("%d lowercase, %d uppercase, %d other", lc, uc, oc);
        return 0;
    }
    
  6. 该程序将不停重复打印下面一行:
    You are 65. Here is your gold watch.
    问题出在这一行:if (age = 65)
    这行代码把age设置为65,使得每次迭代的测试条件都为真。

  7. 下面是根据给定输入的运行结果:

    q

    Step 1

    Step 2

    Step 3c

    Step 1h

    Step 1

    Step 3b

    Step 1

    Done
    注意,b和#都可以结束循环。但是输入b会使得程序打印step 1,而输入#则不会。

  8. 下面是一种解决方案:

    #include <stdio.h>
    int main(void)
    {
        char ch;
        while ((ch = getchar()) != '#')
        {
            if (ch != '\n')
            {
                printf("Step 1\n");
                if (ch == 'b')
                    break;
                else if (ch != 'c')
                {
                    if (ch != 'h')
                         printf("Step 2\n");
                    printf("Step 3\n");
                }
            }
        }
        printf("Done\n");
        return 0;
    }
    

编程练习

  1. 编写一个程序读取输入,读到#字符停止,然后报告读取的空格数、换行符数和所有其他字符的数量。

  2. 编写一个程序读取输入,读到#字符停止。程序要打印每个输入的字符以及对应的ASCII码(十进制)。每行打印8个“字符-ASCII码”组合。建议:使用字符计数和求模运算符(%)在每8个循环周期时打印一个换行符。

  3. 编写一个程序,读取整数直到用户输入0。输入结束后,程序应报告用户输入的偶数(不包括0)个数、这些偶数的平均值、输入的奇数个数及其奇数的平均值。

  4. 使用if else语句编写一个程序读取输入,读到#停止。用感叹号替换句号,用两个感叹号替换原来的感叹号,最后报告进行了多少次替换。

  5. 使用switch重写练习4。

  6. 编写程序读取输入,读到#停止,报告ei出现的次数。
    注意
    该程序要记录前一个字符和当前字符。用“Receive your eieio award”这样的输入来测试。

  7. 编写一个程序,提示用户输入一周工作的小时数,然后打印工资总额、税金和净收入。做如下假设:
    a.基本工资 = 10.00美元/小时
    b.加班(超过40小时) = 1.5倍的时间
    c.税率:前300美元为15%
    续150美元为20%
    余下的为25%
    用#define定义符号常量。不用在意是否符合当前的税法。

  8. \8. 修改练习7的假设a,让程序可以给出一个供选择的工资等级菜单。使用switch完成工资等级选择。运行程序后,显示的菜单应该类似这样:


    Enter the number corresponding to the desired pay rate or action:

    1. $8.75/hr

    2. $9.33/hr

    3. $10.00/hr

    4. $11.20/hr

    5. quit


    如果选择1~4其中的一个数字,程序应该询问用户工作的小时数。程序要通过循环运行,除非用户输入5。如果输入1~5以外的数字,程序应提醒用户输入正确的选项,然后再重复显示菜单提示用户输入。使用#define创建符号常量表示各工资等级和税率。

  9. 编写一个程序,只接受正整数输入,然后显示所有小于或等于该数的素数。

  10. 1988年的美国联邦税收计划是近代最简单的税收方案。它分为4个类别,每个类别有两个等级。下面是该税收计划的摘要(美元数为应征税的收入):
    例如,一位工资为20000美元的单身纳税人,应缴纳税费0.15×17850+0.28×(20000−17850)美元。编写一个程序,让用户指定缴纳税金的种类和应纳税收入,然后计算税金。程序应通过循环让用户可以多次输入。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTXWvWjS-1613808959729)(D:%5CQQ%E6%96%87%E4%BB%B6%5CMobileFile%5CIMG_0464(20210220-144752)].PNG)

  11. ABC邮购杂货店出售的洋蓟售价为2.05美元/磅,甜菜售价为1.15美元/磅,胡萝卜售价为1.09美元/磅。在添加运费之前,100美元的订单有5%的打折优惠。少于或等于5磅的订单收取6.5美元的运费和包装费,5磅~20磅的订单收取14美元的运费和包装费,超过20磅的订单在14美元的基础上每续重1磅增加0.5美元。编写一个程序,在循环中用switch语句实现用户输入不同的字母时有不同的响应,即输入a的响应是让用户输入洋蓟的磅数,b是甜菜的磅数,c是胡萝卜的磅数,q是退出订购。程序要记录累计的重量。即,如果用户输入4磅的甜菜,然后输入5磅的甜菜,程序应报告9磅的甜菜。然后,该程序要计算货物总价、折扣(如果有的话)、运费和包装费。随后,程序应显示所有的购买信息:物品售价、订购的重量(单位:磅)、订购的蔬菜费用、订单的总费用、折扣(如果有的话)、运费和包装费,以及所有的费用总额。


  1. #include <stdio.h>
    #define END '#'
    int main(void)
    {
        char ch;
        unsigned enter = 0u, space = 0u, count = 0u;
        printf("enter character(s)(%c to quit):", END);
        while ((ch = getchar()) != END)
        {
            if (ch == '\n')
            {
                enter++;
            }
            else if (ch == ' ')
            {
                space++;
            }
            else
            {
                count++;
            }
        }
        printf("space:%u,enter:%u,other:%u\n", space, enter, count);
        return 0;
    }
    
  2. #include <stdio.h>
    #include<ctype.h>
    #define END '#'
    int main(void)
    {
        char ch;
        int count = 0;
        printf("enter characters and %c to quit: ", END);
        while ((ch = getchar()) != END)
        {
            if(!isprint(ch))
            {
                continue;
            }
            if (count % 8 == 0 && count != 0)
            {
                printf("\n");
            }
            count++;
            printf("%c : %d\t", ch, ch);
        }
        return 0;
    }
    
  3. #include <stdio.h>
    int main(void)
    {
        int num;
        unsigned int even = 0u, odd = 0u;
        int sum_even = 0, sum_odd = 0;
        while (scanf("%d", &num) && num)
        {
            /* code */
            if (!(num % 2))
            {
                even++;
                sum_even += num;
            }
            else
            {
                odd++;
                sum_odd += num;
            }
        }
        printf("Average odd number of %u is %g\nAverage even number of %u is %g\n", odd, (float)sum_odd / odd, even, (float)sum_even / even);
        return 0;
    }
    
  4. #include <stdio.h>
    #include <string.h>
    #define END '#'
    int main(void)
    {
        char ch[256] = "";
        int i = 0;
        printf("enter characters and %c to quit: ", END);
        unsigned int count = 0u;
        while ((ch[i] = getchar()) != END)
        {
            if (ch[i] == '.')
            {
                ch[i] = '!';
                count++;
            }
            else if (ch[i] == '!')
            {
                ch[i++] = '!';
                count++;
            }
            i++;
        }
        for (int j = 0; j < strlen(ch) - 1; j++)
        {
            printf("%c", ch[j]);
        }
        printf("\n");
        printf("%u次\n",count);
        return 0;
    }
    
  5. #include <stdio.h>
    #include <string.h>
    #define END '#'
    int main(void)
    {
        char ch[256] = "";
        int i = 0;
        printf("enter characters and %c to quit: ", END);
        unsigned int count = 0u;
        while ((ch[i] = getchar()) != END)
        {
            switch (ch[i])
            {
            case '.':
                ch[i] = '!';
                count++;
                break;
            case '!':
                ch[i++] = '!';
                count++;
                break;
            default:
                break;
            }
            i++;
        }
        for (int j = 0; j < strlen(ch) - 1; j++)
        {
            printf("%c", ch[j]);
        }
        printf("\n");
        printf("%u次\n", count);
        return 0;
    }
    
  6. #include <stdio.h>
    #include <stdbool.h>
    #define END '#'
    int main(void)
    {
        char prev = '\0', current = '\0';
        char ch;
        unsigned count = 0u;
        while (true)
        {
            scanf("%c", &ch);
            if(ch == END)
            {
                break;
            }
            if (current == '\0')
            {
                current = ch;
            }
            else
            {
                prev = current;
                current = ch;
                if (current == 'i' && prev == 'e')
                {
                    count++;
                }
            }
        }
        printf("ei 出现 %u 次",count);
        return 0;
    }
    
  7. #include <stdio.h>
    #define BASETIME 40
    #define BASE 10
    #define BASEM (10 * 40)
    #define BI 1.5
    
    #define SHUIBASE 300
    #define SHUIBASE1 450
    
    #define SHUIRATE 0.15
    #define SHUIRATE2 0.2
    #define SHUIRATE3 0.25
    int main(void)
    {
        unsigned time;
        printf("enter the number of work time: ");
        scanf("%u", &time);
        float money;
        money = time >= BASETIME ? (BASEM + (time - BASETIME) * BI * BASE) : (BASE * time);
        float rate;
        if (money <= SHUIBASE)
        {
            rate = money * SHUIRATE;
        }
        else if (money <= SHUIBASE1)
        {
            rate = SHUIBASE * SHUIRATE + (money - SHUIBASE) * SHUIRATE2;
        }
        else
        {
            rate = SHUIBASE * SHUIRATE + (SHUIBASE1 - SHUIBASE) * SHUIRATE2 + (money - SHUIBASE1) * SHUIRATE3;
        }
        printf("shuiqian: %g,shui: %g,shuihou: %g\n", money, rate, money - rate);
        return 0;
    }
    
  8. #include <stdio.h>
    #include <stdbool.h>
    #define BASETIME 40
    #define BI 1.5
    
    #define SHUIBASE 300
    #define SHUIBASE1 450
    
    #define SHUIRATE 0.15
    #define SHUIRATE2 0.2
    #define SHUIRATE3 0.25
    int main(void)
    {
        float base;
        while (true)
        {
            printf("*****************************************************************\n");
            printf("Enter the number corresponding to the desired pay rate or action:\n");
            printf("1) $8.75/hr\n");
            printf("2) $9.33/hr\n");
            printf("3) $10.00/hr\n");
            printf("4) $11.20/hr\n");
            printf("5) quit\n");
            printf("*****************************************************************\n");
            printf("please enter option number: ");
            int option;
            scanf("%d", &option);
            switch (option)
            {
            case 1:
                base = 8.75;
                break;
            case 2:
                base = 9.33;
                break;
            case 3:
                base = 10.00;
                break;
            case 4:
                base = 11.20;
            case 5:
                printf("Exit!\n");
                return 0;
                break;
            default:
                printf("Error!Repeat!\n");
                continue;
                break;
            }
            unsigned time;
            printf("enter the number of work time: ");
            scanf("%u", &time);
            float money;
            money = time >= BASETIME ? (BASETIME * base + (time - BASETIME) * BI * base) : (base * time);
            float rate;
            if (money <= SHUIBASE)
            {
                rate = money * SHUIRATE;
            }
            else if (money <= SHUIBASE1)
            {
                rate = SHUIBASE * SHUIRATE + (money - SHUIBASE) * SHUIRATE2;
            }
            else
            {
                rate = SHUIBASE * SHUIRATE + (SHUIBASE1 - SHUIBASE) * SHUIRATE2 + (money - SHUIBASE1) * SHUIRATE3;
            }
            printf("shuiqian: %g,shui: %g,shuihou: %g\n", money, rate, money - rate);
        }
    }
    
  9. #include <stdio.h>
    #include <stdbool.h>
    bool isPrime(int num);
    int main(void)
    {
        printf("enter a number(>0): ");
        unsigned int num;
        scanf("%u", &num);
        for (int i = 2; i <= num; i++)
        {
            if (isPrime(i))
                printf("%u ", i);
        }
        printf("\n");
        return 0;
    }
    bool isPrime(int num)
    {
        bool isprime = true;
        for (int i = 2; i * i <= num; i++)
        {
            if (!(num % i))
                isprime = false;
        }
        return isprime;
    }
    
  10. #include <stdio.h>
    #include <stdbool.h>
    
    #define SHUIBASE1 17850
    #define SHUIBASE2 23900
    #define SHUIBASE3 29750
    #define SHUIBASE4 14876
    
    #define SHUIRATE1 0.15
    #define SHUISUPPERBASE1 0.28
    
    int main(void)
    {
        printf("%f\n", 0.15 * 17850 + 0.28 * (20000 - 17850));
        float base;
        while (true)
        {
            printf("*****************************************************************\n");
            printf("Enter the number corresponding to your type:\n");
            printf("1) single\n");
            printf("2) Head of household\n");
            printf("3) Married\n");
            printf("4) Divorce\n");
            printf("5) quit\n");
            printf("*****************************************************************\n");
            printf("please enter option number: ");
            int option;
            scanf("%d", &option);
            switch (option)
            {
            case 1:
                base = SHUIBASE1;
                break;
            case 2:
                base = SHUIBASE2;
                break;
            case 3:
                base = SHUIBASE3;
                break;
            case 4:
                base = SHUIBASE4;
            case 5:
                printf("Exit!\n");
                return 0;
                break;
            default:
                printf("Error!Repeat!\n");
                continue;
                break;
            }
            float money;
            printf("enter the number of money: ");
            scanf("%f", &money);
    
            float rate;
            /* if (rate <= base)
            {
                rate = money * SHUIRATE1;
            }
            else
            {
                rate = base * SHUIRATE1 + (money - base) * SHUISUPPERBASE1;
            } */
            rate = money <= base ? money * SHUIRATE1 : base * SHUIRATE1 + (money - base) * SHUISUPPERBASE1;
            printf("shuiqian: %g,shui: %g,shuihou: %g\n", money, rate, money - rate);
        }
    }
    
  11. #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdbool.h>
    #include <ctype.h>
    
    #define YANG 2.05
    #define TIAN 1.15
    #define HU 1.09
    
    #define ZHEBASE 100
    #define BAOBASE1 5
    #define BAOBASE2 20
    
    #define ZHE 0.05
    #define BAOMBASE1 6.5
    #define BAOMBASE2 14
    #define BAOMBASE3 0.5
    int main(void)
    {
    	float yang = 0.0f, tian = 0.0f, hu = 0.0f;
    	float weight;
    	while (true)
    	{
    		char option;
    		char ch;
    		printf("*****************************************************************\n");
    		printf("Enter the number corresponding to your type:\n");
    		printf("a) 洋蓟\n");
    		printf("b) 甜菜\n");
    		printf("c) 胡萝卜\n");
    		printf("q) end\n");
    		printf("*****************************************************************\n");
    
    		printf("enter the character of option: ");
    		while (true)
    		{
    			// 处理用户输入的在缓冲区内现在剩余非选项字符。
    			// 但是还不能应对用户随便输入任何选项
    			// 可以应用字符数组,每次循环开始初始化数组,将用户的本次输入存入数组,处理就只处理字符串的第一个字母,但是好累啊,不搞了。
    			ch = getchar();
    			if (ch=='a'||ch=='b'||ch=='c'||ch=='q')
    			{
    				break;
    			}
    		}
    		// scanf("%c", &option);
    		if (isalpha(option))
    			option = tolower(ch);
    
    		switch (option)
    		{
    		case 'a':
    			printf("enter the weight of yangji: ");
    			scanf("%f", &weight);
    			yang += weight;
    			break;
    		case 'b':
    			printf("enter the weight of tiancai: ");
    			scanf("%f", &weight);
    			tian += weight;
    			break;
    		case 'c':
    			printf("enter the weight of huluobo: ");
    			scanf("%f", &weight);
    			hu += weight;
    			break;
    		case 'q':
    			printf("You have selected all!\n");
    			goto jisuan;
    			break;
    		}
    	}
    jisuan:
    	float m_yang, m_tian, m_hu, transport, discount;
    	printf("yangji: %g$ / p You selected %g p.\n", YANG, yang);
    	printf("tiancai: %g$ / p You selected %g p.\n", TIAN, tian);
    	printf("huluobo: %g$ / p You selected %g p.\n", HU, hu);
    	m_yang = yang * YANG;
    	m_tian = tian * TIAN;
    	m_hu = hu * HU;
    	float m_sum = m_hu + m_tian + m_yang;
    	float w_sum = yang + hu + tian;
    	printf("The money of all vegetable is %g.\n", m_sum);
    	discount = m_sum >= ZHEBASE ? m_sum * ZHE : 0;
    	if (discount)
    		printf("The discount is %g.\n", discount);
    
    	if (w_sum == 0)
    	{
    		transport = 0;
    	}
    	else if (w_sum <= BAOBASE1)
    		transport = BAOMBASE1;
    	else if (w_sum <= BAOBASE2)
    		transport = 14;
    	else
    		transport = 14 + (w_sum - 20) * BAOMBASE3;
    	printf("The money of transporting is %g.\n", transport);
    	printf("The money of all is %g", m_sum - discount + transport);
    	return 0;
    }
    
    		printf("enter the character of option: ");
    		while (true)
    		{
                // 处理缓存中存在的空字符,直到遇到非空字符
    			ch = getchar();
    			if (!isspace(ch))
    			{
    				break;
    			}
    		}
    		// scanf("%c", &option);
    		if(isalpha(ch))
    			option = tolower(ch);
    		
    		switch (option)
    		{
    		case 'a':
    			printf("enter the weight of yangji: ");
    			scanf("%f", &weight);
    			yang += weight;
    			break;
    		case 'b':
    			printf("enter the weight of tiancai: ");
    			scanf("%f", &weight);
    			tian += weight;
    			break;
    		case 'c':
    			printf("enter the weight of huluobo: ");
    			scanf("%f", &weight);
    			hu += weight;
    			break;
    		case 'q':
    			printf("You have selected all!\n");
    			goto jisuan;
    			break;
    		default:
    			printf("Error!Repeat!\n");
    			break;
    		}
    	}
    jisuan:
    	float m_yang, m_tian, m_hu, transport, discount;
    	printf("yangji: %g$ / p You selected %g p.\n", YANG, yang);
    	printf("tiancai: %g$ / p You selected %g p.\n", TIAN, tian);
    	printf("huluobo: %g$ / p You selected %g p.\n", HU, hu);
    	m_yang = yang * YANG;
    	m_tian = tian * TIAN;
    	m_hu = hu * HU;
    	float m_sum = m_hu + m_tian + m_yang;
    	float w_sum = yang + hu + tian;
    	printf("The money of all vegetable is %g.\n", m_sum);
    	discount = m_sum >= ZHEBASE ? m_sum * ZHE : 0;
    	if (discount)
    		printf("The discount is %g.\n", discount);
    
    	if (w_sum == 0)
    	{
    		transport = 0;
    	}
    	else if (w_sum <= BAOBASE1)
    		transport = BAOMBASE1;
    	else if (w_sum <= BAOBASE2)
    		transport = 14;
    	else
    		transport = 14 + (w_sum - 20) * BAOMBASE3;
    	printf("The money of transporting is %g.\n", transport);
    	printf("The money of all is %g", m_sum - discount + transport);
    	return 0;
    }
    
上一篇:Go流程控制


下一篇:goto语法在PHP中的使用