目录
7.2.1 另一个示例:介绍getchar()和putchar()
7.1 if语句
找出0℃以下的天数占总天数的百分比
#include<stdio.h>
int main()
{
const int FREEZING = 0;
float temperature;
int cold_days = 0;
int all_days = 0;
printf("Enter the list of daily low temperature");
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;
}
scanf("%f",&temperature)==1 非常典型的输入值安全控制
这里对于数据的运算处理暗藏玄机,该程序示例把计算后的百分比强制转换为float类型,其实,也没有必要使用强制类型转换,由于C的自动转换类型规则,乘积会被强制转换成浮点数。但是强制类型转换可以明确表达转换类型的意图,保护程序免受不同版本编译器的影响
if语句的通用形式:
if (expression)
statement
如果expression的值为真(非0),则执行statement;否则,跳过statement。
7.2 if else 语句
if else的形式:
if (expression)
statement1
else
statement2
如果expression为真,执行statement1,否则执行statement2.
注意,statement可以是一条简单语句或复合语句。
7.2.1 另一个示例:介绍getchar()和putchar()
getchar()函数不带任何参数,它从输入队列中返回下一个字符
putchar()函数打印它的参数
与scanf(),printf()函数的区别:
局限性:getchar()和putchar() 只处理字符
优秀的方面:
他们比更通用的scanf(),printf()函数更快、更简洁。
getchar()putchar()不需要转换说明
PS:这两个函数是定义在stdio.h头文件中(而且,它们通常是预处理宏,而不是真正的函数)
7.2.2 ctype.h系列的字符函数
字符映射函数(toupper (),tolower() )不会修改原始的参数,在程序里只会返回已修改的值
7.2.3 多重选择 else if(略)
C99标准要求编译器最少支持127层套嵌
7.2.4 else与if配对
就近原则:else与它上面的离他 最近的未配对的if 配对。
PS:编译器是忽略缩进的
7.2.5 多层嵌套的if语句 (略)
7.3 逻辑运算符
在C语言中,逻辑运算符用于对布尔表达式进行操作,常用的逻辑运算符有三种:与(&&)、或(||)和非(!)。
- 与(&&):当且仅当两个操作数都为真时,结果才为真。如果任何一个操作数为假,那么结果就为假。
- 或(||):只要有一个操作数为真,结果就为真。只有当两个操作数都为假时,结果才为假。
- 非(!):用于对操作数取反,如果操作数为真,则结果为假;如果操作数为假,则结果为真。
逻辑运算符通常用于条件判断语句中,例如if语句、while循环等,用于组合多个条件,以便根据这些条件确定程序的执行流程。
7.3.1备选拼写:iso646.h头文件
C99标准新增了可替代逻辑运算符的拼写,它们被定义在iso646.h头文件中,使用后便可以用and代替&&,or代替||,not代替!
7.3.2优先级
-
逻辑非(NOT) -
!
- 优先级最高,因为它是对单个操作数的操作。
-
逻辑与(AND) -
&&
- 优先级次之,用于比较两个操作数是否都为真。
-
逻辑或(OR) -
||
- 优先级最低,用于检查两个操作数中是否至少有一个为真。
7.3.3 求值顺序
C保证了逻辑表达式的求值顺序是从左往右,&&和||运算符都是序列点(即在程序从一个运算对象到下一个之前,所有的副作用都会生效)C保证了一旦发现某个元素让整个表达式无效,便立即停止求值
7.3.4 范围
&&可用于测试范围。如判断score是否在90和100之间,可写成:
if (score>=90 && score<=100)
注意不要写成数学上的 90<= score<=100,因为程序里会先算(90<=score)得到的结果是0或1,然后用0或1和100比较,结果1<=100 总是成立,结果总为1.
7.4 一个统计单词的程序
#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 p_lines = 0; //不完整的行数
int n_words = 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;
}
在我们编辑对输入内容判断时可以利用转义字符来表示输入行为的动作或者说是格式
7.5 条件运算符 ? :
C语言里唯一的三元运算符
条件运算符是表达if else的一种简单方式
形式为: expression1 ? expression2 : expression3
如果expression1为真。则整个表达式的值为expression2的值,否则为二学expression3的值。
每一个运算对象都是一个表达式
常见的例子:
取两个数的较大值:
max = (a>b) ? a :b;
7.6 循环辅助: continue 和break
7.6.1 continue语句
在循环中使用continue语句,会跳过本次迭代的剩余部分,开始下一轮
在嵌套循环内,则只会影响包含该语句的内层循环
continue还可用作占位符
7.6.2 break语句
在循环中使用continue语句,会终止包含它的循环。
嵌套循环中使用continue和break对本层循环有效。
break还可用于因其他原因退出循环的情况
7.7 多重选择: swithc和break
7.7.1 switch语句
运行处理过程
表达式是对刚输入的给的值,然后程序扫描标签(case 'a';case 'b')列表,直到发现一个匹配的值为止,然后跳转到哪一行继续执行,如果没找到相匹配的标签,如果有default标签行,就跳转并执行;否则程序继续执行在switch后面的语句
形式:
switch(整型表达式)
{
case 常量1:
语句
case 常量2:
语句
default:
语句
}
break 语句可以运用在switch语句和循环里,而continue则只能在循环里使用
原因:
在 switch
语句中,没有 "迭代" 的概念,因为 switch
语句不是基于迭代的控制结构。switch
语句用于基于不同条件执行不同的代码块,而不是重复执行某个过程。
因此,continue
在 switch
语句中没有意义,因为它不能跳过 switch
语句的剩余部分并开始下一次迭代。相反,break
在 switch
语句中用于防止 "fall-through",这是控制流程的一种方式,与循环中的 break
作用相似。
fall-through现象
"Fall-through" 是 switch
语句中的一种行为,当一个 case
块执行完毕后,如果没有遇到 break
语句,程序会继续执行下一个 case
块的代码,直到遇到一个 break
语句或者 switch
语句结束。这种现象有时是故意为之,有时则是编程错误。
故意的 "Fall-through"
有时程序员会故意使用 "fall-through" 来减少代码的重复。例如,如果多个 case
标签需要执行相同的代码,可以在第一个 case
后省略 break
,让后续的 case
标签 "fall-through" 到这段代码。
switch (ch) {
case 'a':
case 'A':
printf("Character is 'a' or 'A'.\n");
// 故意省略 break
case 'b':
case 'B':
printf("Character is 'b' or 'B'.\n");
break; // 这里使用 break 以防止进一步 fall-through
default:
printf("Other character.\n");
break;
}
在这个例子中,'a'
和 'A'
都会执行相同的代码,然后 "fall-through" 到 'b'
和 'B'
的处理代码,除非在处理 'a'
或 'A'
后遇到 break
。
非故意的 "Fall-through"
非故意的 "fall-through" 通常是由于程序员忘记在 case
块的末尾添加 break
语句。这可能导致程序执行了不应该执行的代码,从而产生错误的行为。
switch (num) {
case 1:
printf("One\n");
// 忘记 break
case 2:
printf("Two\n");
break;
case 3:
printf("Three\n");
break;
default:
printf("Other\n");
break;
}
在这个例子中,如果 num
的值为 1,程序不仅会打印 "One",还会继续执行并打印 "Two",因为缺少了 break
语句。
防止 "Fall-through"
为了防止非故意的 "fall-through",程序员通常会在每个 case
块的末尾添加 break
语句,除非有意为之。此外,一些编程风格指南和代码审查过程也会强调在 case
块后使用 break
,以确保代码的清晰性和正确性。
使用 break
可以明确地告诉其他程序员(以及未来的自己)每个 case
块的结束点,减少因 "fall-through" 导致的逻辑错误。这也是为什么 break
在 switch
语句中非常重要,而 continue
则没有这样的用途。
7.7.3 多重标签
switch(ch)
{
case 'a': case 'A':a_cnt ++;
break;
case'b': case 'B' :b_cnt++;
break;
}
7.8 goto语句
goto 语句包含两部分:标签名和 goto。
part2: printf("go to part2.\n"); //part2是标签名,可以使用goto跳到这里
goto part2; //跳到part2这一行,并执行语句
但是除了多重循环时break无法跳出全部循环时,可以借助goto来跳出循环
其他情况下没有必要(最好不要)使用goto.