当前位置: 首页 > article >正文

《C语言程序设计现代方法》note-3 选择语句 循环语句

助记提要

  1. 关系运算符、判等运算符、逻辑运算符的优先级和结合性;
  2. 条件运算符;
  3. C语言中如何使用布尔值;
  4. switch语句的注意;
  5. for语句省略表达式;
  6. 逗号表达式;
  7. goto语句;
  8. 空语句;

不应该以聪明才智和逻辑分析能力来评判程序员,而要看其分析问题是否全面。

5章 选择语句

5.1 逻辑表达式

选择语句必选先测试表达式的值是真还是假。
构建逻辑表达式的运算符有9个。
逻辑表达式的值只会是1(真)或0(假)。

  • 关系运算符
    大于>、小于<、大于等于>=、小于等于<=

4个关系运算符都是左结合的,优先级低于算术运算符。

注意 i < j < k在C语言中是合法的,但是并不是检测j是否位于i和k之间。它先比较i<j的值,然后用这个结果和k比较。

  • 判等运算符
    等于==、不等于!=

判等运算符是左结合的,优先级低于关系运算符。
表达式i < j == j < k,当i < jj < k同为真或同为假时,表达式的值为真。

==经常被误写为=,有的时候编译器不会警告,导致程序出现错误。使用gcc编译C程序时可以指定-Wall选项,强制编译器检查这类书写错误。程序员可以在if条件外面多加一层圆括号来禁用该警告:if ((i = j)) ...

  • 逻辑运算符
    !、与&&、或||

是一元的右结合运算符,优先级和正负号相同。
&&||是二元的左结合运算符,优先级低于判等运算符。

运算符&&||都会进行短路计算:先计算出左操作数的值,然后计算右操作数。如果表达式的结果可以由左操作数推导出来,就不计算右操作数。

注意 由于&&||的短路特性,有些运算不一定会发生,如:i > 0 && ++j > 0,如果i不大于0,j的自增操作就不会发生。

5.2 if语句

语法
  • 基本语法格式
if (表达式) 语句;

先计算表达式的值,表达式值非零,就执行圆括号后面的语句。
C语言中,非零值都为真。

  • 复合语句
if (表达式){
	语句1;
	语句2;
	...
}

花括号包起来的语句称为复合语句,它会强制编译器将一组语句做为一条语句来处理。
为语句添加花括号,可以使程序更易读。也可以更方便地向语句块中添加语句。

  • else子句
if (表达式) 语句1; else 语句2;

if (表达式) {
	语句11;
	语句12
} else {
	语句21;
	语句22;
}

注意 else子句会归属于离它最近且还未和其他else匹配的if语句。
下面的这种写法,按缩进来理解,else应该属于外层的if语句,但是实际上它是属于内层的if语句的。这时最好把内层if语句使用花括号包起来。

if (y != 0)
	if (x != 0)
		result = x / y;
else
	printf("Error: y == 0\n");
  • 级联if语句
    级联式if语句可以避免数量过多的内嵌if语句
if (表达式) 
	语句1;
else if (表达式)
	语句2;
else
	语句3;

级联式if语句不是新的语句类型,只是将另外一个if语句做为else字句。

条件运算符

条件运算符的作用类似if语句,它允许根据条件的结果,产生两个值中的一个。条件运算符构建的条件表达式语法如下:

表达式1 ? 表达式2 : 表达式3

如果表达式1成立,就计算表达式2的值做为条件表达式的结果,否则计算表达式3的值做为结果。
条件运算符是唯一的一个三元运算符。它的优先级低于所有的运算符。

条件表达式短小但是难以阅读,应该避免使用。但在少数地方使用会很方便:

// return语句使用
return i > j ? i : j

// printf函数使用
printf("%d\n", i > j ? i : j);

条件表达式i > 0 ? i : j中,如果i是int型,j是float型,即使i > 0为真,表达式的结果也是i转换后的float型。

C语言中的布尔值

C89没有定义布尔类型,但是很多程序中需要变量存储真或假值。
程序员经常使用TRUE和FALSE这样的名字来定义宏:

#define TRUE 1
#define FALSE 0

使用时,可以通过这两个宏去赋值。判定条件时可以这样:

if (flag == TRUE) ...
// 如果flag为真
if (flag) ...
// 如果flag为假
if (!flag) ...

第二种方式更好,因为当flag的值不是0或1时程序也能正确运行。

或者定义一个可以用作类型的宏:

#define BOOL int
BOOL flag;

C99中提供了_Bool类型,用来声明布尔变量。_Bool变量实际上就是整型变量,但是只能赋值为0或1,存储非0值会导致变量被赋值为1。

_Bool flag;

C99还提供了一个新的头<stdbool.h>,这个头提供了宏bool,用来代表_Bool;还提供了true代表1,false代表0。
引用这个头文件,可以很方便地使用布尔值:

bool flag;
flag = false;
flag = true;

5.3 switch语句

需要把表达式和一系列的值进行比较时,可以使用级联式的if语句或switch语句。

switch (表达式){
  case 常量表达式1: 语句1;
  ...
  case 常量表达式n: 语句n;
  default : 语句;
}

switch后面的表达式必须是整型表达式,不能是浮点数和字符串。
case:常量表达式称为分支标号。其中常量表达式也必须是整型表达式,不能包含变量和函数调用。
分支标号只能带一个常量表达式,但是分支标号后面可以跟任意数量的语句,并且不需要花括号括起来。每组语句的最后一句通常是break语句。
分支标号不允许重复,但是没有顺序要求,default分支不是必须写在最后。多个分支标号可以放在同一组语句的前面。
default分支可以没有。

  • break语句的作用
    switch语句实际上是一种基于计算的跳转。控制表达式求值时,控制会跳转到与switch表达式的值相匹配的分支标号处。分支标号只是一个说明switch内部位置的标记,不影响程序控制流。执行完这个分支的最后一条语句后,程序会忽略分支标号,向下跳转到下一个分支的第一条语句上。
    break语句会让控制跳转到switch语句的下面。

switch的最后一个分支不需要break语句,但是也会加一个,以防后续增加分支数目时忘记写break语句。

6章 循环

6.1 while语句

表达式为真,就执行后面的语句。持续执行,直到表达式为假。

while (表达式) 语句;

表达式后面的语句称为循环体。循环体可以有多条语句,使用花括号括起来即可。

如果控制表达式始终为真,while语句会无限循环下去。需要的时候常常用非零常量来构造无限循环。

while (1) ...

6.2 do语句

do语句先执行循环体,然后计算控制表达式的值,如果表达式是非零的,就再次执行循环体,然后再次计算表达式的值。直到表达式的值为0,就终止do语句。

do 语句 while (表达式);

循环体可以是一条语句,也可以是花括号包起来的复合语句。
使用时最好给所有的do语句都加上花括号,因为没有花括号的do语句容易被误认为while语句。

6.3 for语句

for (声明或表达式1; 表达式2; 表达式3) 语句;

表达式1是循环开始前的初始化步骤,只执行1次;表达式2用来控制循环,表达式2的值不为0,循环就会一直执行下去;表达式3是每次循环中都会最后执行的操作。
由于表达式1和表达式3都是以语句的方式执行的,互不相关,它们经常被用做赋值表达式或自增/自减表达式。

for循环可以使用等价的while循环替换:

表达式1;
while (表达式2) {
	语句;
	表达式3;
}
for语句常用的形式

for语句很适合用在使用计数变量的循环中。for语句实现“向上加”或“向下减”n次的循环的一般格式:

// 向上加,从0加到n-1
for (i = 0; i < n; i++) ...
// 向上加,从1加到n
for (i = 1; i <= n; i++) ...
// 向下减,从n-1到0
for (i = n - 1; i >= 0; i--) ...
// 向下减,从n到1
for (i = n; i > 0; i--) ...

使用时要注意<<=的使用效果,避免循环次数出错。

for循环省略表达式

C语言允许省略for循环的表达式。

省略表达式1,执行循环前没有初始化操作,相关操作需要放到循环前进行。

for (;表达式2;表达式3) ...

省略表达式3,则需要在循环体中确保表达式2最终会变为假。

for (表达式1;表达式2;) ...

同时省略表达式1和表达式3,产生的效果和while语句一样。这时使用while语句更好。

for (;表达式2;) ...

while (表达式2) ...

省略表达式2,默认判定为真,for语句进入无限循环:

for (;;) ...
声明用于循环的变量

C99中,表达式1可以替换为一个声明:

for (int i = 0; i < n; i++) ...

变量i不需要在for语句之前声明。
如果for语句之前已经声明了变量i,表达式1会创建一个新的i仅用于循环内。这样声明的变量不可以在循环外访问。
for语句可以声明多个同类型的变量。

逗号运算符

逗号运算符是为了在C语言要求只能有一个表达式的情况下,可以使用两个或更多表达式。
比如想在for语句中,编写多个初始化表达式,或者在每次循环中对多个变量执行自增操作。

表达式1, 表达式2

逗号表达式先计算表达式1的值,然后扔掉。接着计算表达式2的值,将这个值做为整个表达式的值。
表达式1应该始终都有副作用(更改变量值),否则就没有意义。

for (sum = 0, i = 1; i <= n; i++)
	sum += i;

6.4 退出循环

  • break
    break可以跳出while、do或for循环。
    它适用于需要把退出点设置在循环体中间而不是之前或之后的情况。

break可以把程序控制从包含它的最内层while、do、for或switch语句中转移出去。
这些语句嵌套出现时,break只能跳出一层嵌套。

  • continue
    continue可以跳过某次迭代的部分内容。
    它只能用在循环中,并且把程序控制转移到循环体末尾之前,不跳出循环,直接进入下一次迭代。

  • goto
    breakcontinue都是跳转语句,它们都有特定的跳转点。break跳到包含它的循环结束之后的点,continue跳到循环结束之前的点。
    goto语句可以跳转到函数中任何有标号的语句处。
    标号是放在语句开始处的标识符。

标识符: 语句;
goto 标识符;

执行goto语句时,控制会转移到标识符后面的语句上。该语句必须和goto处于同一个函数。

goto语句在早期的编程语言中很常见,但是日常编程中很少用了。break、continue、return和exit函数可以代替需要使用goto的大多数情况。
goto语句既可以往前跳又可以往后跳,而且多个goto语句可以到达同一个标号,这会使代码难以理解和修改。

goto语句很有用的地方在于,能一次跳出多层嵌套的循环:

while (...) {
	while (...){
		...
		// 这里使用break只能跳出一层
		goto loop_done;
		...
	}
}
loop_done: ...;

6.5 空语句

空语句是除了末尾的分号外什么也没有的语句。

空语句可以用来编写空循环体的循环:

for (d = 2; d < n && n % d != 0; d++)
	;

空语句最好单独放在一行,避免阅读程序时分不清后面的语句是否是循环体。可以使用其它写法凸显空循环体:

for (d = 2; d < n && n % d != 0; d++)
	continue;
	
for (d = 2; d < n && n % d != 0; d++)
	{}

循环转变为空循环体的循环后,通常不会提升效率。

注意 如果不小心在ifwhilefor语句的圆括号后面加了分号,就会创建空语句,导致ifwhilefor语句提前结束。
if语句的圆括号后加分号,条件会失效,后面的语句总会被执行;
while语句的圆括号后加分号,会造成无限循环。即使循环终止,也只执行一次循环体。
for语句的圆括号后加分号,只执行一次循环体。


http://www.kler.cn/a/383448.html

相关文章:

  • 细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV
  • CentOS Stream 9 挂载Windows共享FTP文件夹
  • Golang的容器化技术实践总结
  • 【C++】B2066救援题目分析和解决讲解
  • 基于SSM(Spring + Spring MVC + MyBatis)框架搭建一个病人跟踪信息管理系统
  • 如何查看pad的console输出,以便我们更好的进行调试,查看并了解实际可能的问题。
  • C++(一)
  • 开学轻松逆袭孩子的学习利器培养自律习惯,提高学习效率❗❗让习惯养成更轻松~
  • 【Rust Crate之Actix Web(一)】
  • Sigrity Power SI 3D-EM Inductance Extraction模式如何进行电感的提取操作指导(一)
  • 计算机体系结构知识(二)-gdb和args
  • Linux -- 初识线程
  • 【鉴权】OAuth 2.0: 高度灵活与安全的身份认证框架
  • 百度实习生内推
  • Java实战项目-基于微信小程序的校园生活互助服务小程序
  • 供热的一些基础技术数据
  • 2024年10月全球人工智能领域的重大事件盘点
  • Prompt Engineering介绍
  • AI大模型重塑软件开发流程:定义、应用场景、优势、挑战及未来展望
  • 父组件调用函数式子组件,并向子组件传递函数参数。
  • Web3中的区块链技术:从基础设施到应用的演变
  • Python Matplotlib:基本图表绘制指南
  • 社交电商全球化:开源链动模式的引领与挑战
  • uniapp 整合 OpenLayers - 测距测面
  • 安装mysql主从复制
  • SpringMVC快速上手