ch6 C控制语句:循环
一个好的语言应该能够提供以下三种形式的程序流:
● 顺序执行语句序列(顺序)
● 在满足某个条件之前反复执行一个语句序列(循环)
● 通过进行一个判断在两个可选的语句序列之间选择执行(分支)
建立循环时应该注意这三方面:
●明确定义结束循环的条件
●确保在循环判断中使用的值在第一次使用之前已经初始化
●确保循环在每个周期更新了判断值
顺序结构就是流水一般的代码顺序执行下来,而循环就是本章节的重点while循环。
而分支为下一章节的内容。
OK,开始。
while(条件表达式)
{
循环语句,我认为将循环语句不管是一句都要花括号括起来。
}
while循环,若循环条件表达式值为真则执行,反之不执行。
什么是真,真表达式的值为1,假表达式为0。
在C语言里,非0即为真,C对真的范围很宽,条件值非0就循环,
这使得判断条件是建立在数值的基础上而不是真/假的基础上。
条件表达式可以使用精简的方法:
status = scfan("%ld", &num);
while(status == 1)
{
status = scfan("%ld", &num);
printf("How do you do");
}
/********************************/
while(scfan("%ld",&status) == 1)
{
printf("How do you do");
}
这个样子不是很精简了吗?
但是由于C语言的自由,可能会出现以下几种状况,写程序时应当注意。
▲注意语法:
index = 1;
while (--index < 5)
{
printf("How do you do");
}
/*程序将无休止的打印(会停止,因为减下去会变成整数,详见第三章toobig.c),因为设置了错误的循环条件。*/
index = 10;
while (index++ < 5)
{
printf("How do you do");
}
/*程序将无法打印出来,因为条件永远不会满足。*/
▲注意符号:
int n = 0;
while (n < 3)
printf("n is %d /n",n);
n++;
/*程序少了花括号将while下的两句语句括起来一起执行,致使n变量不能增加导致程序死循环*/
int n = 0;
while (n++ < 3);
printf("n is %d/n",n);
printf("That's is all this progrm does./n");
结果:
n is 4
That's is all this progrm does.
/*单独分号也算一个语句,在C中,单独的分号代表空语句*/
▲真值问题:
int n = 3;
while (n)
printf("%2d is ture/n",n--);
printf("%2d is false/n",n);
n = -3;
while (n)
printf("%2d is ture/n",n++);
printf("%2d is false/n",n);
结果:
3 is ture
2 is ture
1 is ture
0 is false
-3 is ture
-2 is ture
-1 is ture
0 is false
/*得出结论如上面所说的真假*/
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);
/*结果将导致循环的无休止执行,原因在于 while (status = 1) 所以说while值将为真不会停止
当循环语句执行到status = scanf("%ld", &num); 这时status 的值不就重新被赋予了吗?但是
又执行到while (status = 1) while又重新赋值,这将无休无止。*/
/*while (status = 1) 不该在使用== 使用=,前者意思为检查status是否为1,后者则为赋值语句
所以要确保正确的使用运算符*/
新的_Bool类型
C99专门为表示真/假添加了_Bool类型。在编程领域,表示真或假的变量开始时被称为布尔类型(Boolean variable)。
这样_Bool就是布尔变来那个的C类型名。一个_Bool变量值只可以具有值1(真)或0(假)。如果把一个_Bool变量赋为一个非零的数值,变量
就被设置为1。这说明C把任何非零的值都认为真。
input_is_good = (scanf("%ld",&num) == 1);
==运算符返回的值是1或0,把==表达式括起来的圆括号不是必需的,因为==运算符的优先级比=高,但是为了代码的阅读性,保持这个好习惯。
c99还提供一个stdbool.h头文件,包含这个头文件可以使用bool来代替_Bool,并把true和false定义成值为1和0的符号常量。
在程序中包含这个头文件可以写出与C++兼容的代码,因为C++把bool、ture、false定义为关键字。
for循环
for(inteialize; test; update)
statement
for语句使用由分号分开的三个控制表达式来控制循环过程。
initialize表达式只在循环语句执行之前执行一次。然后对test表达式求值。
如果该表达式为真循环就被执行一次,然后计算 update 表达式,接着再次检查test表达式。
for语句是一个入口条件循环,即是否再次执行循环的决定是在循环执行之前作出的。
因此,循环可能一次也不执行。
statement可以单挑语句也可以花括号起来的复合语句。
for的灵活性
▲使用减量运算符来减小计数器
for(secs = 5; secs >0; secs--)
printf("%d seconds! /n",secs);
结果
5 seconds!
4 seconds!
3 seconds!
2 seconds!
1 seconds!
▲让计数器依次增加N
int secs;
for(secs = 2; secs < 60; secs = secs+13)
printf("%d /n", secs);
结果
2
15
28
41
54
▲也可以用字符代替数字进行计算
char secs;
for(secs = 'a'; secs < 'z'; secs ++)
printf("The ASCII value for %c is %d. /n", secs,secs);
结果
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.
▲
如果与限制循环次数相比,更关心限制立方的大小,可以使用这用判断条件。
for (num = 1; num <= 6; num++)
printf("%5d %5d/n",num,num*num*num);
改为
for (num = 1; num*num*num <= 216; num++)
printf("%5d %5d/n",num,num*num*num);
▲也可以让数量集合增加而不是算术增加,也就是说不是加上一个固定的数而是乘上一个固定的数。
double debt;
for (debt = 100.0; debt< 150.0;debt = debt*1.1)
printf("Your debt is now $%.2f./n",debt);
每次增加自身10%
结果
Your debt is now $100.00.
Your debt is now $110.00.
Your debt is now $121.00.
Your debt is now $133.10.
Your debt is now $146.41.
▲甚至可以让一个表达式为空,确保在循环中包含了一些能使循环最终结束的语句。
int ans,n;
ans = 2;
for(n = 3; ans <= 25;)
ans = ans * n;
printf("n = %d; ans = %d./n",n,ans);
return 0;
结果
n = 3; ans = 54.
n一直保持为3,ans开始为2,增加为6、18、54(18小于54所以循环再执行一次得54)。
中间控制表达式为空会被认为是真,所以下面循环会永远执行。
for(;;)
printf("f*ck the word");
▲第一个表达式不必初始化一个变量,他也可以是某种类型的printf()语句,
要记住第一个表达式只在循环的其他部分之前被求值或执行一次。
int num;
for(printf("f*ck the word!/n");num != 6;
scanf("%d",&num));
printf("f * c k/n");
return 0;
结果
f*ck the word!
1
74
7
6
f * c k
退出条件循环:
do while
while循环和for循环都是入口条件循环,在每次执行循环之前先检查判断条件,这样循环中的语句就有可能一次也不执行。
C也有退出条件循环,判断条件在执行循环之前进行检查,这样就可以保证循环语句中的语句至少被执行一次,这样被称为do whle循环。
格式
do
statement
while (expression)
do while语句创建了一个在判断表达式为假之前重复执行的循环。
do while语句是一个退出条件循环,因此循环必须至少被执行一次。
/*
截止目前此章的的3种循环概况已over。
叹..
*/
●选择哪种循环
首先要确定需要的是入口条件循环还是退出条件循环。
通常是需要入口条件循环。
①一般原则是在跳过之前进行查看要比之后好。
②如果在循环开始的地方进行循环判断,程序的可读性更强。
③如果一开始就不满足判断条件,那么跳过整个循环是很重要的。
******************************************************************************
循环的小结
while和for语句提供了入口条件循环,for语句特别适合那些包含有初始化和更新的循环。
逗号运算符可以使在一个for循环中初始化和更新多个变量,do while是退出条件循环。
******************************************************************************
●关系运算符
赋值运算符<关系运算符<算术运算符
关系运算符的优先级:
①高< <= > >=
②低== !=
一个简单的关系表达式由关系运算符及两侧的操作数组成,条件为真,则表达式值为1,假为0。
●赋值运算符
n+=20 等于 n=n+20
n-=20 等于 n=n-20
乘
除
求模
C不要求您使用这些形式,但是他们更加简洁,与更长的形式相比可能产生效率更高的机器代码。
如果想在for 循环里塞进一些复杂的东西时,这些复合赋值运算符就特别有用了。
●逗号运算符
逗号运算符扩展了for循环的灵活性,因为它使您可以再一个for循环中使用多个初始化或更新表达式。
/*例,程序清单6.13 postage*/
#include <stdio.h>
int main (void)
{
const int FRIST_OZ = 37;
const int NEXT_OZ = 23;
int ounces,cost;
printf(" ounces const /n");
for (ounces =1,cost=FRIST_OZ;ounces <=16;ounces++,
cost +=NEXT_OZ)
printf("%5d $%4.2f/n",ounces,cost/100.0);
return 0;
}
结果前三行
ounces const
1 $0.37
2 $0.60
3 $0.83
逗号运算符并不知限于for循环中使用,但是这是最常使用的地方,该运算符还具有两个属性。
首先,他保证了被他分开的表达式按从左到右的次序进行计算,
(逗号是个顺序点,逗号左边产生有的副作用都在程序运行到逗号右边之前生效)
因此, ounces 在 cost 之前初始化。
这不重要,但是在ounces++,cost = ounces * FIRST_OZ
这就递增了ounces 并在第二个子表达式中使用了ounces的新值。
作为顺序点的逗号保证左边子表达式的副作用在计算右边的子表达式之前生效。
x=(y = 3,(z = ++y + 2) + 5);
首先y赋值为3,把y递增为4,再+2,把结果6赋给z,z+5,最后x为11.
n=100,110;
那么n将得到100,这个语法没有错,C把它解释成一个逗号表达式,n=100是左子表达式,而110是右子表达式。
num = (110,100);
printf("%d/n",num);
结果
100
把100赋给num,因为该值是右子表达式的值。
●嵌套循环
嵌套循环(nested loop)是指在另一个循环之内的循环。
#include <stdio.h>
#define ROWS 6
#define CHARS 10
int main (void)
{
int row;
char ch;
for (row = 0; row < ROWS; row++)
{
for (ch = 'A'; ch < ('A' + CHARS);ch++)
printf("%c", ch);
printf("/n");
}
return 0;
}
结果
ABCDEFGHIJ
ABCDEFGHIJ
ABCDEFGHIJ
ABCDEFGHIJ
ABCDEFGHIJ
ABCDEFGHIJ
外部循环 从0~5一共6次,内部循环10次。
*****************************************
#include <stdio.h>
int main (void)
{
const int ROW = 6;
const int CHARS = 6;
int row;
char ch;
for (row = 0; row < ROW; row++)
{
for (ch = ('A' + row); ch < ('A' + CHARS);ch++)
//外部每次值增加1,内部值+1
printf("%c", ch);
printf("/n");
}
return 0;
}
结果
ABCDEF
BCDEF
CDEF
DEF
EF
F
通过修改内部循环的一部分依赖于外部循环,可以使内部循环在每个周期中的表现不同。
●数组
定义方法:
数组类型 数组名[数组数量]
float debt[20];
声明debt是float类型的数组,具有20个元素。
数组第一个元素从算起debt[0]。
给元素赋值方法、数组更具体使用这里不详细了,第10章再深入。
字符串是个特别是例子,它被存储在一个字符数组中。
一般来说,字符数组就是元素都被赋予字符值的数组,如果字符数组包含了空字符/0,
那么字符数组的内容就构成了一个字符串,其中空字符标志串的结尾。
#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);
for (index = 0;index < SIZE;index ++)
scanf("%d",&score[index]);
printf("The scores read in are as follows:/n");
for (index = 0; index < SIZE;index++)
printf("%5d",score[index]);
printf("/n");
for (index = 0;index < SIZE;index++)
sum += score[index];//求和
average = (float)sum /SIZE;//节省时间的方法(是强制转换?)
printf("Sum of scores = %d, average = %.2f./n",sum,average);
printf("That's a handicap of %.0f./n",average - PAR);
return 0;
}
从以上程序可以看到使用了三个独立的for循环,虽然可以为了让程序更紧凑在一个循环中合并多个操作,
但是为了让程序更为紧凑,应该根据模块化(modularity)的原则进行调整,这个术语所蕴含的思想是
程序应该被分为一些单独的单元,每个单元执行一个任务,这样会使程序更易阅读,更要重要的一点是:
不同的部分分开那么模块化可以使程序升级更容易或修改,当了解函数后,就可以把每个单元放入函数中来增强程序的模块化。
●函数
*************************************************************
//6.20计算一个值的整数幂
#include <stdio.h>
double power (double n,int p);
/*ANSI原型 此处为函数声明
函数声明需要分号*/
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)
{
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");
}
printf("Hope you enjoyed this power trip --bye!/n");
return 0;
}
double power (double n,int p)//函数定义 函数定义是不需要分号的
{
double pow = 1;
int i;
for(i = 1;i <= p; i++)
pow *=n;
return pow;
}
***************************************************************
使用一个函数需要完成三个单独是步骤
1。使用函数原型声明该函数
2。在程序中通过函数调用来使用该函数
3。定义函数
OK,首先呢,是函数声明。
double power (double n,int p);
该语句声明了一个double类型叫power的函数,返回值为double类型。
编译器需要知道power()的返回值类型,这样才能知道需要多少字节
的数据以及如何解释他们。
double n,int p
说明power()接受两个参数,分别是double的值和int值。
注意函数声明是有分号的,而下面的函数定义是不用分号的。
double power (double n,int p)
double n,int p在这里为形参分别为double和int型。
下面这句就是函数的调用了:
xpow = power (x,exp);
调用了power函数并传递两个值,函数计算x的exp次幂把结果返回给调用程序,
接着返回值又给变量xpow。
return pow;
返回pow值。
声明函数、调用函数、定义函数、使用return关键字,这些就是定义使用具有
返回值的函数时的基本要素。
如果把函数定义放在main()前面就可以省略声明了,因为编译器在到达main()
时就知道函数信息了,但这不是标准的C风格,因为main()通常提供一个程序
的整体框架,所以最好首先给出main()函数,由于函数经常放在单独文件中,
所以前向声明是必不可少的。
为什么无需声明scanf()函数呢,因为stdio.h头文件中包含了scanf(),printf()
以及一些IO函数的函数声明了。
/*终于搞定了,此章可以打7分,很多话都是摘自书本原来下来的,惭愧,惭愧,
但是个人觉得有点用处吧,偶尔FAQ,偶尔过目怕忘记,其实还的要多多实践*/