目录
6.8 出口条件循环 do while (先做一次,再检查条件)
6.1 再探while循环
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L;
int status;
printf("Please enter an integer to be summed");
printf("(q to Quit)");
status = scanf("%ld",&num);
while (status == 1)
{
sum = sum +num;
printf("Please enter next integer (q to Quit):");
status = scanf("%ld",&num);
}
printf("Those integers sum to %ld.\n",sum);
return 0;
}
6.1.1 程序注释
再次提醒 == 运算符是C的相等运算符
终止原理
在这里利用scanf()的一个特点;即如果用户输入的不是数字(如:q)scanf()会读取失败并返回0;此时,status的值就是0;循环结束。因为输入的字符q不是数字,所以它会被放回输入队列中(实际上,不仅仅是q,任何非数值的数据都会导致循环终止)
EOF
如果 scanf 在读取过程中遇到问题,比如文件结束(EOF,End Of File)或者输入/输出错误,它会返回一个特殊的值 EOF。
当 scanf 返回 EOF 时,它通常意味着读取操作没有成功完成。在循环中使用 scanf 时,如果 scanf 返回 EOF,循环会终止。这是因为循环通常依赖于 scanf 的返回值来确定是否继续读取更多的输入。
scanf()的双重特性
`scanf()` 函数在 C 语言中具有双重特性,它既是一个输入函数,也是一个格式化函数。这两个特性使得 `scanf()` 非常强大,但同时也需要程序员在使用时格外小心。
1. 输入函数:
`scanf()` 的主要目的是从标准输入(通常是键盘)读取用户输入的数据。它可以根据指定的格式字符串解析输入流,并根据这些格式将输入的字符串转换为相应的数据类型,然后存储在提供的变量地址中。
2. 格式化函数:
`scanf()` 通过格式字符串来控制输入数据的解析方式。格式字符串定义了期望的输入格式,包括数据类型、字段宽度、分隔符等。这使得 `scanf()` 能够灵活地处理各种不同的输入情况。
双重特性的体现:
- 数据类型转换:
`scanf()` 根据格式字符串中的格式说明符(如 `%d`、`%f`、`%s` 等)自动将输入的字符串转换为相应的数据类型。例如,`%d` 将输入的数字字符串转换为整数,`%f` 将输入的数字字符串转换为浮点数。
- 输入控制:
格式字符串中的限定符(如宽度限定符 `3` 或 `*`)可以用来控制输入的读取,例如,`%3s` 会读取最多三个字符的字符串,`%*d` 会跳过下一个整数输入。
- 错误处理:
`scanf()` 在遇到输入错误时(如类型不匹配、超出预期的输入等)会有不同的行为。它可能跳过错误的输入,或者返回一个错误标志(通常是 `EOF`),这需要程序员进行适当的错误检查和处理。
使用注意事项:
- 缓冲区溢出:
如果输入的数据超过了为变量分配的空间,可能会导致缓冲区溢出,这是常见的安全漏洞。
- 输入验证:
由于 `scanf()` 会根据格式字符串解析输入,如果输入不符合预期格式,可能会导致未定义的行为。因此,在使用 `scanf()` 时,应该总是检查其返回值,并进行适当的错误处理。
- 安全性:
在处理来自不可信源的输入时,应该谨慎使用 `scanf()`,因为它可能被用来执行格式化字符串攻击。
6.1.2 C风格读取循环
while (scanf("%ld", &num) == 1)
{
/*循环行为*/
}
6.2 while语句
while 循环的通用形式:
while (expression)
statement
while 循环是 入口条件循环,满足条件时才进入循环体。
只有while(expression) 后的单独语句(或者{}扩起的语句块)才是 循环部分
每一次循环都被称为一次迭代
6.2.1 终止while循环
构建while循环时,测试表达式(即while后面的expression)的值 最终要为假。否则,就会出现“死循环”。
可以使用break和if语句来终止循环
6.2.2 何时终止循环
6.2.3 while:入口条件循环
while循环是使用入口条件(entry condition)的有条件循环。入口条件是指,只要满足条件时才进入循环体,不满足时就直接跳过循环。
6.2.4 语法要点
只有测试条件后面的单独语句(大括号扩起的复合语句也是单独语句。空语句; 也是单独语句)才是循环部分。
#include <stdio.h>
int main(void)
{
int n = 0;
while (n < 3)
printf("n is %d\n", n);
n++; //注意这句不是循环体的内容,n++不会执行,n<3一直成立,所以上面的循环会一直进行。
printf("That's all this program does\n");
return 0;
}
有时程序员会故意使用带空语句的while语句原因可能是:
1. 等待硬件操作:
在嵌入式系统或与硬件直接交互的程序中,程序员可能需要等待硬件完成某些操作。在这种情况下,使用空循环可以提供一个简单的等待机制,直到硬件准备好进行下一步操作。
2. 简化代码:
在某些情况下,使用空循环可以简化代码逻辑,尤其是在处理简单的状态机或条件检查时。
3. 避免使用额外的库或工具:
在资源受限的环境中,程序员可能没有可用的高级同步机制,如事件、信号量或条件变量。在这种情况下,空循环可能是实现等待机制的唯一选项。
4. 调试目的:
在调试过程中,程序员可能会使用空循环来模拟延迟或强制程序在某个点上暂停,以便观察程序的行为。
5. 特定算法的实现:
在某些算法中,如某些类型的搜索算法或优化算法,可能需要在找到解决方案之前不断迭代。在这些情况下,空循环可以用来表示这种迭代过程。
6. 依赖于特定硬件或操作系统的行为:
在某些情况下,硬件或操作系统的行为可能依赖于 CPU 的忙等待。例如,某些硬件中断可能需要 CPU 在特定时间内保持忙碌状态。
7. 教育目的:
在教学或演示中,空循环可以用来展示循环结构的基本用法,或者用来说明循环的工作原理。
8. 遗留代码维护:
在维护旧的代码库时,程序员可能会遇到使用空循环的情况。在不改变现有逻辑的情况下,他们可能会保留这些循环,尤其是在不清楚替代方案的长期影响时。
9. 特定编程语言或环境的限制:
在某些编程语言或环境中,可能没有提供更高级的同步或等待机制,因此空循环成为唯一的选择。
10.误解或缺乏经验:
有时,程序员可能由于对更好实践的不了解或缺乏经验而使用空循环。
书上的例子的是为了特定算法的实现(为了跳过整数输入)
6.3 用关系运算符和表达式比较大小
while循环经常依赖测试表达式作比较,这样的表达式被称为 关系表达式(relation expression),
出现在关系表达式中的运算符叫做关系运算符(relation operator)。

在while语句中使用 关系表达式:
while( number < 6)
{
printf("Your number is too small.\n");
scanf("%d",&number);
}
while( ch !='$')
{
count++;
scanf("%c",&ch);
}
while (scanf("%f", &num) ==1)
sum += num;
while语句的关系表达式也可以用于比较字符,比较时使用的是机器字符码
虽然关系表达式也可以比较浮点数,但是要注意:比较浮点数时,尽量只使用<和>
6.3.1 什么是真
简单来说,1是真,0是假
6.3.2 其他真值
所有的非零值都被视为真,只有0被视为假
6.3.3 真值的问题
误用=会导致无限循环
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L;
int status;
printf("Please enter an integer to be summed");
printf("(q to Quit)");
status = scanf("%ld", &num);
while (status = 1)
{
sum = sum + num;
printf("Please enter next integer (q to Quit):");
status = scanf("%ld", &num);
}
printf("Those integers sum to %ld.\n", sum);
return 0;
}
循环形成过程:
1. scanf 在读取输入时,如果读取指定形式的输入失败,就会把无法读取的输入留在输入池中。
2. 当它把 q 当作整数读取失败后,q 就留在了输入池。
3. 下次循环时,scanf 会从输入池上次读取失败的位置(也就是 q 所在的位置)继续读取,导致再次失败,进而形成了无限循环。
6.3.4 新的_Bool类型
- _Bool 是 C99 标准引入的布尔类型,通常只占用 1 个字节的内存。
- bool 是 C++中的布尔类型,在不同的编译器和系统中,其占用的内存大小可能有所不同,但通常也是 1 个字节。
C99提供了stdbool.h头文件,该头文件让bool成为_Bool的别名,而且把true和false分别定义为1和0的符号常量。用了该头文件后写出的代码可以与C++兼容
这也是为什么C语言里_Bool类型我们很少用,反而是C++的bool我们爱不释手的原因
6.3.5 优先级和关系运算符

6.4 不确定循环和计数循环
#include <stdio.h>
int main(void)
{
const int NUMBER = 22;
int count = 1;
while (count <= 10) {
printf("count=%d\n", count);
count++;
}
return 0;
}
6.5 for循环
for的形式:
for (initialize; test; update )
statement
for循环的三个表达式 通常是第一个给计数器赋初始值,第二个表达式表示计数器范围
第三个表达式递增计数器。
实际使用可以灵活的利用三个位置的特点,
第1个表达式只在执行循环嵌执行一次,
第2个表达式可以是任何条件
第3个表达式是可以每次递增、递减、加倍等。
三个表达式可以空缺(但是分号不能缺)
每一个表达式的副作用(如:递增变量)都发生在对下一个表达式求值之前
利用for的灵活性
- 可以使用递减运算符来递减计数器
- 可以让计数器跨数递增
- 可以用字符代替数字计数(ASCII)
- 除了测试迭代次数外,还可以测试其他条件
- 可以让递增的量几何增长,而不是算术增长
- 第3个表达式可以使用任意合法的表达式
- 可以省略一个或者多个表达式(但不能省略分号),只要在循环中包含能结束循环的语句即可
- 第1个表达式不一定是给变量赋初值,也可以使用printf()。记住,在执行循环的其他部分之前,只对第1个表达式求值一个或者执行一次
- 循环体中的行为可以改变循环头中的表达式
6.6 其他赋值运算符(运算符优先级表里有)
6.7 逗号运算符
,逗号运算符扩展了for循环的灵活性,以便循环头中包含更多的表达式jj,
如:
for(ounces = 1, cost = FIRST_OZ; ounces <= 16; ounces++, cost += NEXT_OZ)
printf("%5d $%4.2f\n", ounces, cost/100.0 );
它保证了被它分隔的表达式从左到右求值(换言之,逗号是一个序列点,所以逗号左侧项的所有副作用都在程序执行逗号右侧项之前发生)
6.8 出口条件循环 do while (先做一次,再检查条件)
保证了至少执行循环体中的内容一次
do
statement
while( expression);
6.9 如何选择循环
主要考虑是入口循环还是出口循环,
出口循环选do while , 入口循环for或while
6.10 嵌套循环
循环内的语句也是循环时,就形成嵌套循环。
以简单的二层嵌套为例,外层循环每执行一次,里面的语句(包括循环)就执行一遍。
6.11 数组简介
数组:是按顺序存储的一系列类型相同的值,通过整数下标访问数组中单独的项或元素
用于识别数组元素的数字被称为下标、索引、偏移量。
6.12 使用函数返回值的循环示例
6.12.1 math.h库——pow()幂函数
#include<stdio.h>
#include<math.h>
int main()
{
int a,b,ret;
if(scanf("%d %d",&a,&b)==2){
ret = pow(a,b); //求出a的b次方
printf("%d", ret);
}else printf("输出有误");
return 0;
}
这里复现时可能会出现问题原因:
底数 a为负数并且指数 b 不是整数,将会导致 domain error 错误.
底数 a和指数 b都是 0,会导致 domain error 错误.
底数 a是 0,指数 b 为负数,会导致 domain error 或 pole error 错误.
PS:如果使用 scanf("%d %d",&a,&b) 作为取得数值途径,可能会因为scanf()函数的特性(第四章有讲)而出错(如:b拿不到数组等等)
6.12.2 使用带返回值的函数
在 C 语言中,`return` 语句用于从函数返回一个值给调用者,或者从函数中提前退出。`return` 语句的基本用法取决于函数的返回类型:
1. 返回值:
如果函数声明了返回类型(除了 `void`),则 `return` 语句必须返回一个与函数声明的返回类型相匹配的值。
```c
int add(int a, int b) {
return a + b; // 返回两个整数的和
}
```
2. 无返回值:
如果函数的返回类型是 `void`,则 `return` 语句不返回任何值,它只是从函数中退出。
```c
void printHello() {
printf("Hello, World!\n");
return; // 从函数中退出
}
```
3. 从条件语句返回:
`return` 语句可以在 `if`、`else if`、`else` 或 `switch` 语句中使用,用于根据不同的条件返回不同的值。
```c
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
```
4. 提前退出函数:
在循环或条件语句中使用 `return` 可以提前退出函数。
```c
int find(int *array, int size, int value) {
for (int i = 0; i < size; i++) {
if (array[i] == value) {
return i; // 找到值,返回索引
}
}
return -1; // 未找到值,返回-1
}
```
5. 返回字符串:
对于返回字符串的函数,通常返回一个指向字符数组的指针。
```c
char* getGreeting() {
return "Hello, World!";
}
```
6. 返回多个值:
虽然 C 语言的函数不能直接返回多个值,但可以通过返回一个结构体或使用指针参数来间接返回多个值。
```c
typedef struct {
int x;
int y;
} Point;
Point getOrigin() {
Point p = {0, 0};
return p; // 返回一个点的坐标
}
```
7. 返回错误码:
在很多函数中,`return` 语句用于返回错误码,以指示函数是否成功执行。
```c
int openFile(const char* filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return -1; // 打开文件失败,返回错误码
}
return 0; // 成功打开文件
}
```
使用 `return` 语句时,应该确保返回的值与函数的声明相匹配,并且在函数的控制流结束前正确地返回。如果函数应该返回一个值,但没有使用 `return` 语句,或者返回了一个错误的类型,编译器可能会发出警告或错误。
355

被折叠的 条评论
为什么被折叠?



