关键字:for、while、do while
运算符:<、>、>=、<=、!=、==、+=、*=、-=、/=、%=
函数:fabs()
C语言有3中循环:for、while、do while
使用关系运算符构建控制循环的表达式
其他运算符
循环常用的数组
编写有返回值的函数
一种语言应该提供以下3种形式的程序流
执行语句序列
如果满足某些条件就重复执行语句序列
通过测试选择执行哪一个语句序列(下一章)
目录
6.1 再探while循环
6.1.1 程序注释
6.1.2 C风格读取循环
/* summing.c -- sums integers entered interactively */
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L; /* initialize sum to zero */
int status;
printf("Please enter an integer to be summed ");
printf("(q to quit): ");
status = scanf("%ld", &num);
while (status == 1) /* == means "is equal to" */
{
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;
}
[wlsh@wlsh-MacbookPro] ch06$ clang summing.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Please enter an integer to be summed (q to quit): 44
Please enter next integer (q to quit): 33
Please enter next integer (q to quit): 88
Please enter next integer (q to quit): 121
Please enter next integer (q to quit): q
Those integers sum to 286.
第4章介绍过,scanf()返回成功读项的数量。若果scanf()成功读取一个整数,就把该数存入num并返回1,随后返回值被赋值为status。这样做同时更新了num和status的值,while循环进入下一次迭代。
如果用户输入的字符q不是数字,scanf()就读取失败并返回0。如果scnf()在转换值之前出了问题(检测到文件结尾或遇到硬件问题),会返回一个特殊值EOF(其值为-1),也会引起循环终止。
总之,该程序利用scanf()的双重特性避免了在循环中交互输入时这个棘手的问题。伪代码(preudocode),while循环是入口条件循环。
while(status == 1){
/*循环行为*/
status = scanf("ld",&num);
}
while(scanf("ld",&num) == 1){
/*循环行为*/
}
6.2 while语句
通用形式:expression部分使用关系表达式。如果expression为真,即非0,执行statement部分一次,然后再次判断expression,在expression为假之前,循环的判断和执行一直重复进行。每次循环都是一次迭代(iteration)
while(expression)
statement //分号结尾简单语句;或者花括号扩起来的复合语句
6.2.1 终止while循环
6.2.2 何时终止循环
// when.c -- when a loop quits
#include <stdio.h>
int main(void)
{
int n = 5;
while (n < 7) // line 7
{
printf("n = %d\n", n);
n++; // line 10
printf("Now n = %d\n", n); // line 11
}
printf("The loop has finished.\n");
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang when.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
n = 5
Now n = 6
n = 6
Now n = 7
The loop has finished.
6.2.3 while: 入口条件循环
入口条件(entry condition)、无限循环(infinite loop)、空语句(null statement)
6.2.4 语法要点
6.3 用关系运算符和表达式比较大小
while循环依赖关系表达式(relational expression)作比较,出现在关系表达式中间的运算符叫关系运算符(relational operator)
while(ch != '$')
{
count++;
scanf("%c",&ch);
}
while(scanf("%f",&num)==1)
sum = sum + num;
比较时使用的是机器字符码(假定为ASCII)。不能用关系运算符比较字符串,第11章会介绍如何比较字符串。
// cmpflt.c -- floating-point comparisons
#include <math.h>
#include <stdio.h>
int main(void)
{
const double ANSWER = 3.14159;
double response;
printf("What is the value of pi?\n");
scanf("%lf", &response);
while (fabs(response - ANSWER) > 0.0001)
{
printf("Try again!\n");
scanf("%lf", &response);
}
printf("Close enough!\n");
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
What is the value of pi?
3.14
Try again!
3.14159
Close enough!
#include <math.h>
double
fabs(double x);
long double
fabsl(long double x);
float
fabsf(float x);
浮点数的舍入误差会导致在逻辑上应该相等的两数却不相等。例如3 * 1/3 = 1,但是把1/3表示成小数点后6位数字,则是.999999,不等于1。使用fabs()函数返回一个浮点值的绝对值,可以方便地比较浮点数。
6.3.1 什么是真
/* t_and_f.c -- true and false values in C */
#include <stdio.h>
int main(void)
{
int true_val, false_val;
true_val = (10 > 2); // value of a true relationship
false_val = (10 == 2); // value of a false relationship
printf("true = %d; false = %d \n", true_val, false_val);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang t_and_f.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
true = 1; false = 0
6.3.2 其他真值
// truth.c -- what values are true?
#include <stdio.h>
int main(void)
{
int n = 3;
while (n)
printf("%2d is true\n", n--);
printf("%2d is false\n", n);
n = -3;
while (n)
printf("%2d is true\n", n++);
printf("%2d is false\n", n);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang truth.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
3 is true
2 is true
1 is true
0 is false
-3 is true
-2 is true
-1 is true
0 is false
从数值方面而不是从真/假方面来看测试条件。以下两种情况都是goal是0时才为假。
while(goats != 0) //初学者
while(goats) //常用
6.3.3 真值的问题
// trouble.c -- misuse of =
// will cause infinite loop
#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;
}
[wlsh@wlsh-MacbookPro] ch06$ clang trouble.c
trouble.c:13:19: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
while (status = 1)
~~~~~~~^~~
trouble.c:13:19: note: place parentheses around the assignment to silence this warning
while (status = 1)
^
( )
trouble.c:13:19: note: use '==' to turn this assignment into an equality comparison
while (status = 1)
^
==
分析:while(status = 1)实际上相当于while(1),即循环不会退出,虽然用户输入q,status被设置为0,但是循环的测试条件把status又重置为1,进入下一代迭代。
canoes = 5 //把5赋给canoes
canoes == 5 //检查canoes的值是否为5
5 = canoes //语法错误
5 == canoes //检查canoes的值是否为5
C语言不允许给常量赋值,编译器会把赋值运算符的这种用法作为语法错误标记出来。经验丰富的程序员在构建比较是否相等的表达式时,习惯把常量放在左侧。
6.3.4 新的Bool类型
// boolean.c -- using a _Bool variable
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L;
_Bool input_is_good;
printf("Please enter an integer to be summed ");
printf("(q to quit): ");
input_is_good = (scanf("%ld", &num) == 1);
while (input_is_good)
{
sum = sum + num;
printf("Please enter next integer (q to quit): ");
input_is_good = (scanf("%ld", &num) == 1);
}
printf("Those integers sum to %ld.\n", sum);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang boolean.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Please enter an integer to be summed (q to quit): 123
Please enter next integer (q to quit): 23
Please enter next integer (q to quit): q
Those integers sum to 146.
在C语言中,用int类型的变量表示真/假。C99专门针对这种类型的变量新增_Bool类型。表示真或假的变量称为布尔变量(Boolean variable),如果把其他非零数值赋值给_Bool类型的变量,该变量设置为1。
C99提供stdbool.h头文件,该文件让bool成为_Bool的别名,而且还把true和false分别定义为1和0的符号常量,包含该文件后,写出的代码可以与C++兼容。
6.3.5 优先级和关系运算符
6.4 不确定循环和计数循环
不确定循环(indefinite loop)
计数循环(counting loop)
// sweetie1.c -- a counting loop
#include <stdio.h>
int main(void)
{
const int NUMBER = 22;
int count = 1; // initialization
while (count <= NUMBER) // test
{
printf("Be my Valentine!\n"); // action
count++; // update count
}
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang sweetie1.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Be my Valentine!
Be my Valentine!
Be my Valentine!
Be my Valentine!
Be my Valentine!
while (count++ <= NUMBER)
初始化计数器;计数器与有限值比较;每次循环递增计数器
递增发生在循环的末尾 vs 测试和更新放在一起
6.5 for循环
// sweetie2.c -- a counting loop using for
#include <stdio.h>
int main(void)
{
const int NUMBER = 22;
int count;
for (count = 1; count <= NUMBER; count++)
printf("Be my Valentine!\n");
return 0;
}
for循环把(初始化、测试和更新)组合在一起。
#include <stdio.h>
int main(void)
{
char ch;
for(ch = 'a'; ch <= 'z'; ch++)
printf("The ASCII value for %c is %d.\n",ch,ch);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang test.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
The ASCII value for a is 97.
The ASCII value for b is 98.
The ASCII value for c is 99.
The ASCII value for d is 100.
The ASCII value for e is 101.
The ASCII value for f is 102.
The ASCII value for g is 103.
The ASCII value for h is 104.
The ASCII value for i is 105.
The ASCII value for j is 106.
The ASCII value for k is 107.
The ASCII value for l is 108.
The ASCII value for m is 109.
The ASCII value for n is 110.
The ASCII value for o is 111.
The ASCII value for p is 112.
The ASCII value for q is 113.
The ASCII value for r is 114.
The ASCII value for s is 115.
The ASCII value for t is 116.
The ASCII value for u is 117.
The ASCII value for v is 118.
The ASCII value for w is 119.
The ASCII value for x is 120.
The ASCII value for y is 121.
The ASCII value for z is 122.
字符在内部是以整数形式存储的,该循环实际上仍是用整数来计数。
6.6 其他赋值运算符:+=、-=、*=、/=、%=
组合形式的负值运算符,代码更加紧凑,与一般形式相比,组合形式的运算符省车给你的机器代码更高效。
6.7 逗号运算符
// postage.c -- first-class postage rates
#include <stdio.h>
int main(void)
{
const int FIRST_OZ = 46; // 2013 rate
const int NEXT_OZ = 20; // 2013 rate
int ounces, cost;
printf(" ounces cost\n");
for (ounces=1, cost=FIRST_OZ; ounces <= 16; ounces++,
cost += NEXT_OZ)
printf("%5d $%4.2f\n", ounces, cost/100.0);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang postage.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
ounces cost
1 $0.46
2 $0.66
3 $0.86
4 $1.06
5 $1.26
6 $1.46
逗号表达式保证被分割的表达式从左往右求值。即逗号是一个序列点。
/* zeno.c -- series sum */
#include <stdio.h>
int main(void)
{
int t_ct; // term count
double time, power_of_2;
int limit;
printf("Enter the number of terms you want: ");
scanf("%d", &limit);
for (time=0, power_of_2=1, t_ct=1; t_ct <= limit;
t_ct++, power_of_2 *= 2.0)
{
time += 1.0/power_of_2;
printf("time = %f when terms = %d.\n", time, t_ct);
}
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang zeno.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Enter the number of terms you want: 10
time = 1.000000 when terms = 1.
time = 1.500000 when terms = 2.
time = 1.750000 when terms = 3.
time = 1.875000 when terms = 4.
time = 1.937500 when terms = 5.
time = 1.968750 when terms = 6.
time = 1.984375 when terms = 7.
time = 1.992188 when terms = 8.
time = 1.996094 when terms = 9.
time = 1.998047 when terms = 10.
6.8 出口条件循环
C语言有出口条件循环(exit-condition loop),即在循环的每次迭代之后检车测试条件,保证至少执行循环体中的内容一次。
do
statement
while(expression); //分号结尾
6.9 如何选择循环
确定是入口条件循环 还是 出口条件循环
while(scans("%ld",&num)==1)
涉及索引计数的循环,用for循环更合适
for(count = 1; count <= 100; count++)
6.10 嵌套循环
6.10.1 程序分析
6.10.2 嵌套变式
// rows2.c -- using dependent nested loops
#include <stdio.h>
int main(void)
{
const int ROWS = 6;
const int CHARS = 6;
int row;
char ch;
for (row = 0; row < ROWS; row++)
{
for (ch = ('A' + row); ch < ('A' + CHARS); ch++)
printf("%c", ch);
printf("\n");
}
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang rows2.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
ABCDEF
BCDEF
CDEF
DEF
EF
F
6.11 数组简介
用于识别数组元素的数字被称为下标(subscript)、索引(indice)或偏移量(offset)
// scores_in.c -- uses loops for array processing
#include <stdio.h>
#define SIZE 10
#define PAR 72
int main(void)
{
int index, score[SIZE];
int sum = 0;
float average;
printf("Enter %d golf scores:\n", SIZE); //方便处理大小为size的数组
for (index = 0; index < SIZE; index++)
scanf("%d", &score[index]); // read in the ten scores
printf("The scores read in are as follows:\n");
for (index = 0; index < SIZE; index++)
printf("%5d", score[index]); // verify input
printf("\n");
for (index = 0; index < SIZE; index++)
sum += score[index]; // add them up
average = (float) sum / SIZE; // time-honored method
printf("Sum of scores = %d, average = %.2f\n", sum, average);
printf("That's a handicap of %.0f.\n", average - PAR);
return 0;
}
[wlsh@wlsh-MacbookPro] ch06$ clang scores_in.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Enter 10 golf scores:
99 95 109 105 100
96 98 93 99 97 98
The scores read in are as follows:
99 95 109 105 100 96 98 93 99 97
Sum of scores = 991, average = 99.10
That's a handicap of 27.
scanf()会跳过空白字符,使用空格或换行符隔开每个数字。因为输入是缓冲的只有当,用户Enter键数字后才会被发送给程序。
6.12 使用函数返回值的循环示例
6.12.1 程序分析
// power.c -- raises numbers to integer powers
#include <stdio.h>
double power(double n, int p); // ANSI prototype//编译器知道power()返回值类型,才知道有多少字节的数据,以及如何解释它们
int main(void)
{
double x, xpow;
int exp;
printf("Enter a number and the positive integer power");
printf(" to which\nthe number will be raised. Enter q");
printf(" to quit.\n");
while (scanf("%lf%d", &x, &exp) == 2)//成功读取2个值,则返回2
{
xpow = power(x,exp); // function call
printf("%.3g to the power %d is %.5g\n", x, exp, xpow);
printf("Enter next pair of numbers or q to quit.\n"); //q与scanf()中的转换说明%lf不匹配,scanf()会使返回值为0 输入2.8 返回值为1
}
printf("Hope you enjoyed this power trip -- bye!\n");
return 0;
}
double power(double n, int p) // function definition
{
double pow = 1;
int i;
for (i = 1; i <= p; i++)
pow *= n;
return pow; // return the value of pow
}
[wlsh@wlsh-MacbookPro] ch06$ clang power.c
[wlsh@wlsh-MacbookPro] ch06$ ./a.out
Enter a number and the positive integer power to which
the number will be raised. Enter q to quit.
1.2 12
1.2 to the power 12 is 8.9161
Enter next pair of numbers or q to quit.
q
Hope you enjoyed this power trip -- bye!
6.12.2 使用带返回值的函数
为什么在使用scanf()的返回值之前没有声明scanf() ? 为什么在定义中说明了power()的返回类型为double,还要单独声明这个函数?