C语言KR圣经笔记 3.6 do-while循环 3.7 break和continue 3.8 goto和标号

2023-11-11 08:12

本文主要是介绍C语言KR圣经笔记 3.6 do-while循环 3.7 break和continue 3.8 goto和标号,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3.6 do-while循环

如第1章所述,while 和 for 在循环顶部检查结束条件。与之相反,C语言的第三个循环,do-while 是每轮循环的主体走完之后,在底部检查结束条件;循环体至少会执行一次。

do 的语法为

do

        语句

while (表达式);

先执行语句,然后对表达式求值。如果为真,则继续执行语句,接着再对表达式求值,就这样循环往复。当表达式变为假时,循环结束。除了检查的含义外,do-while 等价于Pascal的 repeat-until语句【Pascal的 until 判断为真则循环退出,与C语言的 while 表达式判断为假才退出,含义正好相反】

经验显示 do-while 用得比 while 和 for 少得多。然而,有时它也是有价值的,比如下面这个函数 itoa,将一个数转换成字符串( atoi 的反向处理)。这活儿比你一开始可能想的要稍微复杂些,因为用简单方法生成的字符串顺序是错误的。我们选择反向生成字符串,然后将其翻转。【reverse函数见上一节】

/* itoa: 把n转换成字符串,存入s */
void itoa(int n, char s[])
{int i, sign;if ((sign = n) < 0)   /* 记录符号 */n = -n;           /* 使n为正数 */do {                /* 倒序生成字符串 */s[i++] = n % 10 + '0';     /* 获取下一个数位 */} while ((n /= 10) > 0)        /* 删除该数位 */if (sign < 0)s[i++] = '-';s[i] = '\0';reverse(s);
}

这里用 do-while 是必要的,至少是方便的,因为至少要有一个字符被放到数值 s 中,即使 n 是 0。尽管大括号不是必需的,我们还是把构成 do-while 主体的单条语句包在其中,避免草率的读者把 while 部分错看成是一个 while 循环的开始

练习3-4、在数字用2的补码表示的机器上,我们的 itoa 版本无法处理最大的负数,即当 n = -(2的字长 -1 次方)。解释为何如此。修改程序使之打印正确的值,不管它跑在什么机器上。

练习3-5、写函数 itob(n ,s, b) 将整数 n 转换成以 b 为基数的字符串并存入字符串 s 中。特别说明,itob(n, s, 16) 把 n 格式化为 16进制数存入s中。

练习3-6、写个一版接受3个而不是2个参数的 itoa 函数。第三个参数为最小域宽度;有必要的话,在转换后的数字左侧填充空格使其达到足够的宽度。

3.7 break和continue

不通过顶部或底部的条件检查而直接从循环中退出,有时会比较方便。break 语句提供了从 for、while 和 do 中提前退出的方法,还包括前面说过的 switch 。break 会使它所在的最内层循环或 switch 马上退出。

下面的函数 trim 从字符串末尾删除结尾的空格,制表符和换行符,当发现最右边的非空格、非制表符、非换行符时,它使用 break 从循环中退出。

/* trim: 删除末尾的空格、指标和换行符 */
int trim(char s[])
{int n;for (n = strlen(s)-1; n >= 0; n--)if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')break;s[n+1] = '\0';return n;
}

strlen 返回字符串的长度。for 循环从末尾开始反向扫描,寻找第一个非空格、非制表、非换行符。当找到一个这样的字符,或者 n 变为负数时(此时整个字符串都扫描完了),循环结束。你应该验证,即使在字符串为空或者只包含空白字符的时候,这个处理也是正确的。

continue 语句与 break 相关,但用的少一些;它会使其所在的 for、while 和 do 循环开始下一轮迭代。在 while 和 do 中,这意味着马上执行括号内的检查部分;而对于 for, 控制转移到递增的步骤【即表达式3】。continue 语句只用于循环,不用于 switch。若循环内有 switch 且 switch 内有 continue 时,这个 continue 会使循环进入下个迭代。

例如,下面这个代码段只处理数组 a 中的非负元素;负值都被跳过了

for (i = 0; i < n; i++) {if (a[i] < 0)    /* 跳过负元素 */continue;...   /* 处理正元素 */
}

 continue 语句经常用在这种情况:当循环后面的一部分非常复杂,而把测试条件反转然后再加一层缩进,会让程序嵌套太深【而影响阅读/维护】

3.8 goto 和 标号

C提供了可被无限滥用的 goto 语句,以及跳转的标号。正式地说,goto 并非必要,而且实践中总是很容易写出不用 goto 的代码。本书中我们还没用过 goto。

然而,还是存在一些也许可以使用 goto 的场景。最常用的是从某些深深嵌套的结构中放弃处理,比如从两层或更多层的循环中马上跳出来。不能直接使用 break 语句是因为它只能退出最内层的循环。像这样:

for (...)for (...) {if (disaster)goto error;}
...error:善后处理

如果错误处理代码很重要,而且错误会发生在多处,则这个代码组织方式是很方便的。

标号的形式与变量名相同,后面跟着冒号。标号可以加到与 goto 所在同一函数的任一语句前面。标号的作用域是整个函数。

另一个例子是判断两个数组 a 和 b 是否有相同的一个元素。一种可能的写法是:

    for (i = 0; i < n; n++)for (j = 0; j < m; j++)if (a[n] == b[m])goto found;/* 没找到相同元素的处理 */...
found:/* 找到 a[i] == b[j] */...

涉及 goto 的代码总是能写成不带 goto的,尽管可能的代价是一些重复的检查或一个额外的变量。例如,上面的例子改写为

found = 0;
for (i = 0; i < n && !found; i++)for (j = 0; j < m && !found; j++)if (a[n] == b[m])found = 1;
if (found)/* 找到 a[i-1] == b[j-1] */...
else/* 没找到相同的元素 */...

除了这里举出的几个例外,依赖于 goto 语句的代码通常总是比没有 goto 的代码更难理解、更难维护。尽管我们不想在这个问题上说的太武断,但确实看起来 goto 应该少用,如果不是说完全不用的话。

(第三章完)

这篇关于C语言KR圣经笔记 3.6 do-while循环 3.7 break和continue 3.8 goto和标号的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/388725

相关文章

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作

Go语言使用Gin处理路由参数和查询参数

《Go语言使用Gin处理路由参数和查询参数》在WebAPI开发中,处理路由参数(PathParameter)和查询参数(QueryParameter)是非常常见的需求,下面我们就来看看Go语言... 目录一、路由参数 vs 查询参数二、Gin 获取路由参数和查询参数三、示例代码四、运行与测试1. 测试编程路

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

Go语言网络故障诊断与调试技巧

《Go语言网络故障诊断与调试技巧》在分布式系统和微服务架构的浪潮中,网络编程成为系统性能和可靠性的核心支柱,从高并发的API服务到实时通信应用,网络的稳定性直接影响用户体验,本文面向熟悉Go基本语法和... 目录1. 引言2. Go 语言网络编程的优势与特色2.1 简洁高效的标准库2.2 强大的并发模型2.

Go语言使用sync.Mutex实现资源加锁

《Go语言使用sync.Mutex实现资源加锁》数据共享是一把双刃剑,Go语言为我们提供了sync.Mutex,一种最基础也是最常用的加锁方式,用于保证在任意时刻只有一个goroutine能访问共享... 目录一、什么是 Mutex二、为什么需要加锁三、实战案例:并发安全的计数器1. 未加锁示例(存在竞态)

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1