目录
7.1 if 语句
7.2 在if语句中添加else关键字
- 介绍getchar()和putchar()
- getchar()没参数,返回从输入获取的一个字符;putchar()有参数,打印它的参数。
- getchar和putchar只处理字符,所以比scanf和printf两个函数更快,更简洁。而且getchar和putchar不是函数,它们是定义在stdio.h中的宏。
- ANSI C有一系列标准的函数用来分析字符。ctype.h头文件包含了这些函数的原型,这些函数接受一个字符作为参数,如果该字符属于某特定种类则返回非零值(真),否则返回零(假)。注意,函数并不改变原始的参数,它们只返回改变后的值。
- 程序所包含的一个if else语句是另一个if else语句的else语句部分。第二个if else语句被嵌套在第一个里面。为什么不必将被嵌套的if else语句放在花括号中?因为整个if else结构作为一条语句。当然,使用花括号可以更清楚的表明这种特殊格式的含义。如果没有花括号指明,else与和它最接近的一个if相匹配。
- 以前C习惯用int作为布尔值,现在新型的_Bool型变量更符合需求。而且通过包含stdbool.h头文件,可以用bool代替关键字_Bool表示这种类型,并用标识符true和false代替1和0。包含stdbool.h头文件写出来的代码与C++兼容。
7.3 获得逻辑性
C的逻辑运算符:
运算符 | 含义 |
---|---|
&& | 与 |
|| | 或 |
! | 非 |
- C是在使用美式标准键盘的系统上发展而来,不是所有的解盘都有与美式键盘相同的符号。C标准为逻辑运算符增加了可供选择的拼写法,它们在iso646.h头文件里定义。如果包含了该头文件,可以用and代替&&,用or代替||,用not代替!。
- !运算符的优先级很高,高于乘法运算,和增量运算符优先级相同,仅次于圆括号。&&运算符的优先级高于||,这二者的优先级都低于关系运算符而高于赋值运算符。
- 除了两个运算符共享一个操作数的情况以外,C通常不保证复杂表达式的哪个部分首先被求值。C语言预先这种不确定性,以便编译器设计者针对不同的系统做出最有效率的选择。在5.3.6节也有过类似的观点,那节是针对自增(++)自减(–)运算符,应用在表达式和函数参数中的情况。
此处逻辑运算符&&和||的处理是个例外,C保证逻辑表达式是从左至右处理的。&&和||运算符是分界点,也是顺序点,因此在程序从一个操作数前进到下一个操作数之前,所有的副作用都会生效。而且,C保证一旦发现某个元素使表达式总体无效,求值立刻停止
。 - &&运算符可以用于限制范围。因为ASCII连续字母的编码方式是相邻的数值,所以可以通过if(ch>=‘a’&&ch<=‘z’)判断字母。如果某系统不支持ASCII编码方式,采用的其它编码方式,连续的字母可能就不是相邻的数值,那刚刚的判断字母的方式就不可取。最好的方式应该用ctype.h系列中的函数来分析字符。这一大段其实就最后一句是重点,不要忘记ctype.h头文件可以分析字符,感觉这个头文件但凡处理字符都用得上。
7.4 一个统计字数的程序
7.5 条件运算符
C提供一种简写的方式来表示if else语句的一种形式,被称为条件表达式,并使用条件运算符(? :)。通常if else能完成与条件运算符同样的功能,但是条件运算符语句更简洁,并且依赖编译器可以产生更精简的代码。
7.6 循环辅助手段:continue 和 break
一般情况,进入循环体之后到下次判断之前,程序执行循环体中所有的语句。continue和break语句使您可以根据循环体内进行的判断结果来忽略部分循环甚至终止它。
- continue语句
- 可用于三种循环形式。当运行到该语句时,会使循环体中剩余迭代部分被忽略,开始下一次迭代。如果continue语句处于嵌套结构中,那么它仅仅影响包含它的最里层的结构。
- continue的另一个用处是占位符。
while(getchar() != '\n') ; 当程序已经从一行中读取了一些输入并需要跳转到下一行的开始时,使用上面的语句很方便。 问题是孤立的分号难以被注意。如果使用continue,代码就更具可读性。 while(getchar() != '\n') continue;
- break语句
- break语句实际上是switch语句的附属物。循环中的break语句导致程序终止包含它的循环,并进行程序的下个阶段。嵌套循环中的break语句只是使程序跳出里层循环,要跳出外层循环则还需另一个break语句。
7.7 多重选择:switch 和 break
- break导致程序脱离switch语句,跳到switch之后的下一条语句。如果没有break语句,从相匹配的标签到switch末尾的每一条语句都将被处理。
- break语句用于循环和switch语句,continue语句只能用于循环。如果switch语句位于一个循环中,可以把continue用于switch语句的一部分。在这种情况下,就像在其它循环中一样,continue导致程序跳过该循环的其余部分,其中包括switch的其余部分。
- 圆括号中swith判断表达式应该具有整数值(包括char类型)。case标签必须是整型(包括char类型)常量或者整数常量表达式(仅包含整数常量的表达式)。
- 什么时候该使用switch,而什么时候又该用if else结构呢?通常是没有选择的。
- 如果选择是基于求一个浮点型变量或表达式的值,就不能使用switch。因为switch中表达式要具有整型值,case标签必须是整型常量或整数常量表达式。
- 如果变量必须落在某个范围,也不能用switch。if else结构更合适。
- switch和if else结构都能用的情况,用switch程序可以运行的更快,而且占据较少的代码。
7.8 goto 语句
- goto语句包括两个部分:goto 和一个标签名称。标签的命名遵循与命名变量相同的约定。
- C中建议谨慎使用goto语句,甚至不要用。goto语句很容易被滥用,过度使用goto会使程序流程错综复杂。
- goto语句的用法可以被C中其它语句代替。在出现故障时从一组嵌套的循环中跳出(单个break只能跳出最里层的循环)的goto用法,是唯一被许多C专业人员容忍的。我对比了书中介绍的一些goto用法和C中提供的其它语句代替方式,也觉得还是用if else或while等结构实现goto语句的某些功能更容易理解。
7.12 编程练习
如果你认真阅读这本书的话,直到现在这本书中的编程练习还是简单的。当然肯定比前面的更复杂,因为学的内容多了。这章我觉得比较重要的就是,搞清楚你所要实现功能的逻辑关系。整体来说程序都是顺序执行,学到现在出现了分支和跳转,就要理解你想实现的程序中,各个功能之间的逻辑关系,然后用简洁,高效的计算机语言去实现它,还要注意一些细节。比如本章编程练习最后一题,本以为很简单,写完测试才发现,虽然整体逻辑没错,但细节做的不好。不过不要强求第一次写就能面面俱到,程序都是要边测试边修改才能完成的。最后一题代码如下:
习题11
#include <stdio.h>
#include <windows.h>
#define APRICE 1.25
#define BPRICE 0.65
#define CPRICE 0.89
#define DISCOUNT 0.05
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
int num,weight,aweight,bweight,cweight; //订单总重量和各类蔬菜的重量
char ch;
float tot_cost,a_cost,b_cost,c_cost; //总费用,a费用,b费用,c费用
float disc,transp_cost; //折扣费用,运输费用
disc=0;
aweight=bweight=cweight=0;
printf("********************************\n");
printf("输入你需要订购的蔬菜q退出:\n");
printf("a朝鲜蓟 b甜菜 c胡萝卜 q退出\n");
printf("********************************\n");
while ((ch=getchar())) //该循环记录订单中各个蔬菜的重量
{
if (ch=='a'||ch=='b'||ch=='c')
{
printf("请输入要采购的磅数:\n");
scanf("%d",&num);
}
switch (ch)
{
case '\n': //输入的时候以换行符结束,如果没有这行会导致getchar不能正确选择想要的蔬菜
continue;
case 'a':
aweight+=num;
printf("输入你需要订购的蔬菜q退出:\n");
continue;
case 'b':
bweight+=num;
printf("输入你需要订购的蔬菜q退出:\n");
continue;
case 'c':
cweight+=num;
printf("输入你需要订购的蔬菜q退出:\n");
continue;
case 'q':
break;
default:
printf("请输入正确的数据!!!\n");
continue;
}
break;
}
a_cost=aweight*APRICE;
b_cost=bweight*BPRICE;
c_cost=cweight*CPRICE;
weight=aweight+bweight+cweight;
tot_cost=a_cost+b_cost+c_cost;
if(tot_cost>100)
disc=tot_cost*DISCOUNT;
if(weight<=5&&weight!=0)
{
transp_cost=3.50;
}else if (weight>5&&weight<20&&weight!=0)
{
transp_cost=10.0;
}else if(weight!=0)
{
transp_cost=8+weight*0.1;
}
if (weight==0)
{
printf("该订单你没有采购任何东西!!!\n\n");
}else
{
printf("朝鲜蓟 单价:%.2f 重量:%d 费用:%.2f\n",APRICE,aweight,a_cost);
printf("甜菜 单价:%.2f 重量:%d 费用:%.2f\n",BPRICE,bweight,b_cost);
printf("胡萝卜 单价:%.2f 重量:%d 费用:%.2f\n",CPRICE,cweight,c_cost);
printf("总费用:%.2f 总重量:%d 折扣:%.2f 运输费用:%.2f 实际费用:%.2f\n",tot_cost,weight,disc,transp_cost,tot_cost-disc+transp_cost);
}
return 0;
}
上面这段代码中最重要的就是循环选择蔬菜种类,输入磅数,统计个蔬菜的总磅数。这其中一个细节就是,我们通过getchar()函数从输入中读取字符时,可能会读入隐含的换行符’\n’。因为我们通常输入时是以回车键结尾,回车键是个不可打印字符,所以很容易忘记我们不仅仅输入了看的见的,我们想要的可打印字符,还有看不见的回车键’\n’也会被getchar()函数读取。
getchar()操作的是字符,键盘上的都被认为是字符,尤其是不可打印又看不见的字符尤其要注意,它们都能被getchar获取,不是只有字母、数字会被获取。