目录
6.1 再探while 循环
这一小节感觉对我有用的就是一段伪代码的介绍:伪代码是一种用简单的语言来表示程序的方法,它与计算机语言的形式相对应。伪代码有助于设计程序的逻辑。在认为逻辑正确之后,就可以把伪代码翻译成实际的编程代码。伪代码的一个好处就是,可以使你专注于程序的逻辑与组织,不必担心如何用计算机语言来表达你的想法。
6.2 while语句
- 循环体部分可以是一个带分号的简单语句,也可以是花括号中的一个复合语句。不管哪种形式,在语法上都算做一个单独的语句。
- 构造一个while循环时,循环体中必须包含能改变判断表达式的值来使表达式的值最终变为假。否则循环永远不会停止。(实际上也可以通过break和if语句来终止循环,这将在后续章节介绍)
- while循环是使用入口条件的有条件循环,语句部分的执行依赖于是否满足判断表达式的条件。后续的for循环也是入口条件循环,相对的do while循环是退出条件循环。
- 在C中单独的分号代表空语句,有时候程序员有意使用带有空语句的while语句,因为所有的工作都在判断过程中进行。例如:“while(scanf(”%d",&num)==1) ;"
该语句的功能跳过输入直到第一个不为空格或数字的字符
。
6.3 比较大小:使用关系运算符和表达式
- 关系运算符可以用于浮点数,但要小心,在浮点数比较中只能使用<和>。原因就是浮点数是个近似值,舍入误差可能会造成逻辑上应该相等的两个值不等。例如,3和1/3的乘积应该是1.0。但是如果用6位小数来表示1/3,乘积就是0.999999而不等于1。
- 所有的非零值(包括负数)都被认为是真,只有0被认为是假。如果关系表达式为真,它的值就为1;如果为假,它的值就为0。表达式实际上是数值的。
- 在C中表示真假的变量一直由int类型表示。C99专门为这种类型的变量添加了“_Bool”类型。一个_Bool变量只可以具有值1(真)或0(假)。如果你把一个_Bool变量赋为一个非零的数值,变量就被设置为1。。
- C99还提供了一个头文件stdbool.h,包含这个头文件可以用bool代替_Bool,并把true和false定义成值为1和0的符号常量。在程序中包含这个头文件可以写出与C++兼容的代码,因为C++把bool、true和false定义为关键字。
- 关系运算符的优先级要低于+和-在内的算术运算符,但要高于赋值运算符。关系运算符本身也分为两组不同的优先级。
优先级 | 符号 |
---|---|
高优先级的组 | < <= > >= |
高优先级的组 | == != |
6.4 不确定的循环与计数循环
-
建立一个重复执行固定次数的循环时涉及到三个动作:
- (1)必须初始化一个计数器。
- (2)计数器与某个有限的值进行比较。
- (3)每次循环,计数器的值都要递增。
不确定循环在表达式变为假之前,预先不知道要执行多少次此循环。程序清单6.1就是不确定循环。常用的典型for循环是计数循环。但是没有严格限定while语句处理不确定循环,for语句处理计数循环,while和for之间互相转换不难。
6.5 for循环
for循环把三个动作(初始化、测试、更新)放在一起。第一个表达式进行初始化,它在for循环开始时执行一次;第二个表达式是判断条件,每次执行循环之前都要对它进行求值;第三个表达式进行更新,它在每次循环结束时进行计算。
-
利用for的灵活性
- (1)如果需要你可以让计数器依次加2、加10等等。for(n=2;n<60;n=n+10)
- (2)您也可以用字符代替数字来进行计数
- (3)可以判断迭代次数之外的条件 for(num=1;num numnum<=216;num++)
- (4) 在第三个表达式中,您可以使用所需的任何合法表达式。无论使用什么,在每次循环中都会得到更新。for(x=1;y<=75;y=(++x*5)+50)
- (5) 你甚至可以让一个或多个表达式为空(但不要遗漏分号),只需确保在循环中包含一些能使循环最终结束的语句。中间控制表达式为空会被认为是真。
- (6) 第一个表达式不必初始化一个变量,它也可以是某种类型的printf()语句。要记住第一个表达式只在执行循环的其他部分之前被求值或执行一次。
6.6 更多赋值运算符:+=、-=、*=、/=和%=
复合赋值运算符与=具有同样低的优先级。
6.7 逗号运算符
- 逗号运算符扩展了for循环的灵活性,使您可以在for循环中使用多个初始化表达式或更新表达式。
- 逗号运算符有两个属性:首先保证被它分开的表达式按从左到右的顺序计算(换句话说,逗号是个顺序点,逗号左边产生的所有副作用都在程序运行到右边之前生效);其次,整个逗号表达式的值是右边成员的值。
-
逗号也被用作分隔符,在下面两条语句中,逗号被作为
- (1)char ch,date;
- (2)printf(“%d %d”,chimps,chumps);
分隔符
,而不是 运算符
:
6.8 退出条件循环:do while
while循环和for循环都是入口条件循环,在每次执行循环之前都要检查判断条件,这样的循环中的语句有可能一次也不会执行。do while循环中的语句至少要执行一次。
6.9 选择哪种循环
while循环、for循环和do while循环之间可以相互转化的,所以没有严格规定说某种情况一定用哪种循环。每种循环都要自己的特点,for循环用的比较多,因为初始化表达式、判断条件表达式和更新表达式都写在一起,结构比较清楚。我读了本章的理解是:不确定循环推荐用while循环,计数循环推荐用for循环。do while循环的循环体至少执行一次,少数情况可能用这个结构比较方便。
6.10 嵌套循环
内部循环在外部循环的每次单独循环中,都执行了内部循环的所有循环。通过使内部循环的一部分依赖于外部循环,可以使内部循环在每个周期中表现不同。
6.11 数组
- 一个数组就是线性存储的一系列相同类型的值,整个数组有一个单一的名字,单独的项或元素可以使用一个整数索引来访问。
- 一个潜在的错误:出于执行速度的考虑,C并不检查您是否使用了正确的下标,访问数组时可能会因为下标越界,破坏其他数据。
- 一般来说,字符数组就是元素被赋予字符值得数组。如果字符数组包含了空字符’\0’,那么字符数组得内容就构成了一个字符串,其中空字符标志着字符串得结尾。
6.12 使用函数返回值的循环例子
- 声明函数、调用函数、定义函数、使用return关键字,这些就是在定义并使用具有返回值的函数时的基本要素。
- 调用函数前,为什么要声明函数?因为编译器在编译时遇到调用函数的语句需要知道函数返回值类型,这样才能知道需要多少字节的数据以及如何解释它们,所以需要在调用函数前面先声明函数,不过你也可以把定义函数部分,放在main()函数前面,那样不用声明函数部分也能顺利编译,编译器可以从定义函数中得知所需的信息。不过通常不推荐这样,main()函数是整个程序的框架,放在前面结构才清楚。而且大型程序不可能所有函数都在同一文件中,函数经常放在其他文件中,所以前向声明也是必不可少的。
- 原型使编译器检查您是否正确的使用了函数,而定义则规定了函数如何工作。现代的编程习惯是把程序的元素分为接口和实现部分,原型和定义就是这样的例子。接口部分描述了如何使用一个特性,这正是原型所做的;而实现部分说明了采取的具体动作,这正是定义做的。
6.16 编程练习
学到循环阶段,练习题不再是一眼就看出答案,个人觉得比较重要的就是下面几个
习题4
#include <stdio.h>
#include <windows.h>
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
char ch,i,m,n;
int j;
printf("请输入一个大写字母:___\b\b");
scanf("%c",&ch);
for (i='A';i<=ch ;i++ )
{
for (j=0;j<ch-i ;j++ )
{
printf(" ");
}
for (m='A';m<=i ;m++ )
{
printf("%c",m);
}
for (n=i-1;n>='A' ;n-- )
{
printf("%c",n);
}
printf("\n");
}
printf("\n");
return 0;
}
-
解这题需要理解嵌套循环,我觉得书中介绍嵌套循环比较关键的两句话是:
- (1)嵌套循环中,内部循环在外部循环的每次单独循环中都完全执行内部循环的所有循环。
- (2)通过使内存循环的一部分依赖于外部循环,可以使内部循环在每个周期中的表现不同。
习题11
#include <stdio.h>
#include <windows.h>
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
double sum,sum1,sum2;
int i,size,temp;
temp=1;
printf("请输入要计算序列和的项数:");
scanf("%d",&size);
for (i=1;i<=size ;i++ )
{
sum1+=1.0/i;
sum2+=1.0/i*temp;
temp*=-1;
}
sum=sum1+sum2;
printf("1.0+1.0/2.0+1.0/3.0+...%d=%f\n",size,sum1);
printf("1.0-1.0/2.0+1.0/3.0-...%d=%f\n",size,sum2);
printf("1.0+1.0/2.0+1.0/3.0+...%d+1.0-1.0/2.0+1.0/3.0-...%d=%f\n",size,size,sum);
return 0;
}
这题关键就是第二个序列中符号的控制,刚开始我根据书中提示,直接单独写了个函数控制符号。关于这题还有些疑问,百度其它人的实现方式时,才发现自己处理符号挺傻的,应该用复合赋值符*=来处理,更简洁。知识要灵活运用啊。
习题14
#include <stdio.h>
#include <windows.h>
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
char c_data[255],ch;
int i=0;
printf("请输入一串字符,不超过255个,以回车键结束输入:");
do
{
scanf("%c",&ch);
c_data[i]=ch;
i++;
}
while (ch!='\n');
printf("输入的字符串是:");
for (i=i-1;i>=0 ;i-- )
{
printf("%c",c_data[i]);
}
printf("\n");
return 0;
}
习题15
#include <stdio.h>
#include <windows.h>
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
double sum1,sum2;
int i,a=100;
i=0;
sum1=sum2=100;
do
{
sum1+=a*0.1;
sum2+=sum2*0.05;
i++;
printf("第%d年 %16f%16f \n",i,sum1,sum2);
}
while (sum2<=sum1);
printf("第%d年超过了\n",i);
return 0;
}
习题16
#include <stdio.h>
#include <windows.h>
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
double sum=100;
int i=0;
do
{
sum+=sum*0.08;
i++;
sum-=10;
}
while (sum>0);
printf("第%d年清空了账户\n",i);
return 0;
}
最后三题给我的启发是一样,就是不能因为do while循环用的少就忽略它。不知道其它人对while、for和do while三个循环怎么取舍,我以前碰到要用循环的地方,首先考虑的就是for循环,其次用while,一般都不考虑do while。学完这章,今后我遇到要用循环的情况,我一定是先分析,是不确定循环还是计数循环,计数循环就用for,如果是不确定循环,while和do while循环用哪个逻辑更清晰。
做习题14时我用以前的方法考虑的,尝试用for循环去解决,发现本题不是计数循环,而是不确定循环,用while更合适,虽然知道该怎么判断,怎么保存,但就是感觉逻辑不清晰,因为while循环不仅内部需要调用scanf函数,而且外部也需要调用scanf函数,同时还要判断外部scanf输入的第一个字符是不是回车键’\n’,如果是就跳过while循环结束,如果不是就进入while循环开始输入字符到数组中,相应的数组的下标也因为外部的scanf函数需要注意。最后搞得我逻辑混乱,想起了用do while试试。最后才发现虽然三个循环可以互相转换,但是选用合适的循环,能让你思路更清晰,代码更简洁。习题15和16,少了将数据保存到数组这一步,用while循环实现功能,不像习题14那么复杂,但我觉得还是用do while更加合理。