友情链接:C/C++系列系统学习目录
知识总结顺序参考C Primer Plus(第六版)和谭浩强老师的C程序设计(第五版)等,内容以书中为标准,同时参考其它各类书籍以及优质文章,以至减少知识点上的错误,同时方便本人的基础复习,也希望能帮助到大家
最好的好人,都是犯过错误的过来人;一个人往往因为有一点小小的缺点,将来会变得更好。如有错漏之处,敬请指正,有更好的方法,也希望不吝提出。最好的生活方式就是和努力的大家,一起奔跑在路上
文章目录
🚀选择结构设计
⛳一、if语句
if ( expression )
statement
expression称为测试条件,if 和 if else 语句使用测试条件来判断执行哪些语句。所有非零值都被视为 true,零被视为false。测试通常涉及关系表达式(比较两个值)、逻辑表达式(用逻辑运算符组合或更改其他表达式)
如果对expression求值为真(非0),则执行statement;否则,跳过 statement,statement可以是一条简单语句或复合语句。通常,expression 是关系表达式,例如:
if(x>=0)
printf("%d",x);
拓展:
1.C并不要求一定要缩进,但这是标准风格。缩进让根据测试条 件的求值结果来判断执行哪部分语句一目了然。
2.要记住一个通用原则,如果要测试两个条件,应该使用逻辑运算符把两个完整的测试表达式组合起来。例如:
if (a < x < z) // 错误,没有使用逻辑运算符 … if (ch != 'q' && != 'Q') // 错误,缺少完整的测试表达式 … //正确的方式:用逻辑运算符连接两个关系表达式 if (a < x && x < z) // 使用&&组合两个表达式 … if (ch != 'q' && ch != 'Q') // 使用&&组合两个表达式 …
🎈(一)if语句的三种形态
简单形式的if语句可以让程序选择执行一条语句,或者跳过这条语句。 C还提供了if else形式,可以在两条语句之间作选择。但是如果要在if和else之间执行多条语句,必须用花括号把这些语句括起来成为一个块。
if ( expression )
statement1
else
statement2
1.if语句(如果…那么)
#include <stdio.h>
int main(void) {
int salary;
printf("你月薪是多少");
scanf("%d",&salary);
if (salary < 20000) {
printf("你是一个好人, 我还不想谈恋爱\n");
}
return 0;
}
2.if else语句(如果…那么…否则)
#include <stdio.h>
int main(void) {
int salary;
printf("你月薪是多少");
scanf("%d",&salary);
if (salary < 20000) {
printf("你是一个好人, 我还不想谈恋爱\n");
} else {
printf("我没有什么要求, 只要你对我好就行\n");
}
return 0;
}
3.else if语句(如果…那么…,或者…那么…,否则…)
#include <stdio.h>
int main(void)
{
int salary;
char houseRet[8]; //是否有房
char carRet[8]; //是否有车
printf("你月薪是多少");
scanf("%d",&salary);
printf("你有房吗");
scanf("%s",carRet);
printf("你有车吗");
scanf("%s",carRet);
if (salary < 20000) {
printf("你是一个好人, 我还不想谈恋爱\n");
} else if (houseRet == "有") {
printf("我其实没有什么要求,只要你对我好\n");
} else if (carRet == "有") {
printf("我还不错哦, 以后再联系\n");
} else {
printf("有缘再见\n");
}
return 0;
}
🎈(二)if语句的嵌套
实例: 求 3 个数的最大值
#include <stdio.h>
int main(void) {
int x, y, z;
printf("请输入 3 个整数:\n");
scanf("%d%d%d",&x,&y,&z);
if (x > y) {
if (x > z) {
printf("最大值是:%d\n",x);
} else {
printf("最大值是:%d\n",z);
}
} else {
if (y > z) {
printf("最大值是:%d\n",y);
} else {
printf("最大值是:%d\n",z);
}
}
return 0;
}
嵌套的常见错误(配对错误),配对规则:如果没有花括号,else与离它最近的if匹配,除非最近的if被花括号括起来,错误避免方法:
- 严格使用 { }
- 先写{}再写里面的内容
- 保持良好的“缩进”
⛳二、switch语句循环结构设计
使用条件运算符和 if else 语句很容易编写二选一的程序。然而,有时程序需要在多个选项中进行选择。可以用if else if…else来完成。但是,大多数情况下使用switch语句更方便。
switch语句的作用是根据表达式的值,使流程跳转到不同的语句。switch语句的一般形式如下:
switch(表达式)
{
case 常量表达式1:
语句1
case 常量表达式2:
语句2
case 常量表达式3:
语句3
default 表达式1:
语句1
}
例如:
# include <stdio.h>
int main( ){
char grade;
scanf(" %c", &grade);
printf(" Your score:" );
switch(grade)
{
case 'A': printf("85~ 100\n") ;break;
case 'B': printf("70~ 84\n"); break;
case 'C': printf("60~69\n"); break;
case 'D': printf("< 60\n") ; break;
default: printf("enter data error!\n" );
}
return 0;
}
-
程序分析:等级grade定义为字符变量,从键盘输人一个大写字母,赋给变量grade,switch得到grade的值并把它和各case中给定的值(‘A’,‘B’,‘C’,‘D’之一)相比较,如果和其中之一相同(称为匹配),则执行该case后面的语句(即printf语句)。输出相应的信息。如果输入的字符与’A’,‘B’,‘C’,‘D’ 都不相同,就执行default 后面的语句,输出“输入有错”的信息
-
可以没有default标号,此时如果没有与switch表达式相匹配的case常量,则不执行任何语句,流程转到switch语句的下一个语句。
-
各个case标号出现次序不影响执行结果。例如可以先出现default标号,再出现“case ‘D’: …”,然后是“case ‘B’: …”
-
switch在圆括号中的测试表达式的值应该是一个整数值(包括char类型)。case标签必须是整数类型(包括char类型)的常量或整型常量表达式 (即,表达式中只包含整型常量)。不能用变量作为case标签。
-
case标号只起标记的作用。在执行switch语句时,根据switch表达式的值找到匹配的入口标号,并不在此进行条件检查,在执行完一个case标号后面的语句后,就从此标号开始执行下去,不再进行判断。例如在上面例子中,如果在各case子句中没有break 语句,将
连续输出:Your score:85~ 100 70~ 84 60~ 69 60 enter data error !
-
break语句在其中起什么作用?它让程序离开switch语句,跳至 switch 句后面的下一条语句。如果没有break语句,就会从匹配标签开始执行到switch末尾,如第4点中的情况。一般情况下,在执行一个case子句后,应当用break语句使流程跳出switch结构,即终止switch语句的执行。最后一个case子句(今为default 子句)中可不必加break语句,因为流程已到了switch结构的结束处。
-
在case子句中虽然包含了一个以上执行语句,但可以不必用花括号括起来,会自动顺序执行本case标号后面所有的语句。当然加上花括号也可以。
-
多重标签:多个case标号可以共用一组执行语句,例如:当grade 的值为’A’,‘B’ ,'C’时都执行同一组语句,输出“≥60”。然后换行。
case ' A': case 'B' : case 'C' : printf("> 60\n" ) ; break;
拓展:
1.以下错误,在 vs/vc 中有提示,但是仍可以通过编译。 在 gcc 编译器中,不能通过编译
switch(c) { case 1: int x = 0; //错误! printf("c=1"); break; case 2:....... //应修改为 case 1: { int x = 0; printf("c=1"); } break; case 2:......
2.continue语句:
break语句可用于循环和switch语句中,但是continue只能用于循环中。尽管如此,如果switch语句在一个循环中,continue便可作为switch语句的一部分。
这种情况下,就像在其他循环中一样,continue让程序跳出循环的剩余部分,包括switch语句的其他部分。
⛳三、switch和if else的选择
何时使用switch?何时使用if else?经常会别无选择
如果是根据浮点类型的变量或表达式来选择,就无法使用 switch。如果根据变量在某范围内决定程序流的去向,使用 switch 就很麻烦,这种情况用if就很方便:
if (integer < 1000 && integer > 2)
使用switch要涵盖以上范围,需要为每个整数(3~999)设置case标签。但是,如果使用switch,程序通常运行快一些,生成的代码少一些。
⛳四、拓展:goto语句
早期版本的BASIC和FORTRAN所依赖的goto语句,在C中仍然可用。但是C和其他两种语言不同,没有goto语句C程序也能运行良好。Kernighan和 Ritchie提到goto语句“易被滥用”,并建议“谨慎使用,或者根本不用”
goto语句有两部分:goto和标签名。标签的命名遵循变量命名规则,如下所示:
goto part2
要让这条语句正常工作,函数还必须包含另一条标为part2的语句,该 语句以标签名后紧跟一个冒号开始:
part2: printf("Refined analysis:\n");
避免使用goto:
实际上,break和continue是goto的特殊形式。使用break和 continue 的好处是:其名称已经表明它们的用法,而且这些语句不使用标签,所以不用担心把标签放错位置导致的危险。
程序中使用其他形式比使用goto的条理更清晰。当多种情况混在一起时,这种差异更加明显。过度地使用 goto 语句,会让程序错综复杂。如果不熟悉goto 语句,就不要使用它
但是,C程序员可以接受一种goto的用法——出现问题时从一组嵌套循 环中跳出(一条break语句只能跳出当前循环),例如:
while (funct > 0)
{
for (i = 1, i <= 100; i++)
{
for (j = 1; j <= 50; j++)
{
其他语句
if (问题)
goto help;
其他语句
}
其他语句
}
其他语句
}
其他语句
help: 语句
🚀循环结构设计
⛳一、三种基本循环
🎈(一)while语句实现循环
while循环的通用形式如下:
while ( expression )
statement
- statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来的复合语句。
- expression部分是循环条件。执行循环体的次数是由循环条件控制的,它也称为循环条件表达式、测试表达式。当此表达式的值为“真”(以非0值表示)时,就执行循环体语句;为“假”(以0表示)时,就不执行循环体语句。
- 每次循环都被称为一次迭代
- while循环是使用入口条件的有条件循环。所谓“有条件”指的是语句部分的执行取决于测试表达式描述的条件,如(index < 5)。该表达式是一个入口条件(entry condition),因为必须满足条件才能进入循环体。
终止while循环:
while循环有一点非常重要:在构建while循环时,必须让测试表达式的值有变化,表达式最终要为假。否则,循环就不会终止(实际上,可以使用 break和if语句来终止循环)
死循环:
当循环判断条件永远为真的时候即是一个死循环
while(1) {
语句
}
注意:
1.使用while时,要牢记一点:只有在测试条件后面的单独语句(简单语句或复合语句)才是循环部分。读者应该养成无论是简单语句还是复合语句都用花括号括起来的习惯
2.即使while语句本身使用复合语句,在语句构成上,它也是一条单独的语句。该语句从while开始执行,到第1个分号结束。在使用了复合语句的情况下,到右花括号结束。
3.有时候测试条件表达式已经满足了我们的需求,所以循环体部分为空语句,即一个单独的分号。测试条件后面的单独分号是空语句(null statement),它什么也不做。在C语言中,单独的分号表示空语句。有时,程序员会故意使用带空语句的while语句,因为所有的任务都在测试条件中完成了,不需要在循环体中做什么,如:
//假设你想跳过输入到第1个非空白字符或数字,可以这样写:
while (scanf("%d", &num) == 1)
;
//只要scanf()读取一个整数,就会返回1,循环继续执行。
应该让这个分号独占一行,不要直接把它放在测试表达式同行。这样做一方面让读者更容易看到空语句,一方面也提醒自己和读者空 语句是有意而为之。处理这种情况更好的方法是使用continue 语句。
拓展:
1.伪代码
把sum初始化为0 提示用户输入数据 读取用户输入的数据 当输入的数据为整数时, 输入添加给sum, 提示用户进行输入, 然后读取下一个输入 输入完成后,打印sum的值
伪代码(pseudocode),是一种用简单的句子表示程序思路的方法,它与计算机语言的形式相对应。伪代码有助于设计程序的逻辑。确定程序的逻辑无误之后,再把伪代码翻译成实际的编程代码。使用伪代码的好处之一是,可以把注意力集中在程序的组织和逻辑上,不用在设计程序时还要分心如何用编程语言来表达自己的想法。例如,可以用缩进来代表一块代码,不用考虑C的语法要用花括号把这部分代码括起来。
2.C风格读取循环status = scanf("%ld", &num); while (status == 1) { /* 循环行为 */ status = scanf("%ld", &num); }
可以用以下代码替换
while (scanf("%ld", &num) == 1) { /*循环行为*/ }
第二种形式同时使用scanf()的两种不同的特性。首先,如果函数调用成功,scanf()会把一个值存入num。然后,利用scanf()的返回值(0或1,不是 num的值)控制while循环,因为每次迭代都会判断循环的条件,所以每次迭代都要调用scanf()读取新的num值来做判断
3.与关系表达式结合
许多C程序员都会很好地利用测试条件的这一特性。例如:
while (goats != 0) //替换为: while (goats)
因为表达式goats != 0和goats都只有在goats的值为0时才为0或假。第1种形式(while (goats != 0))对初学者而言可能比较清楚,但是第2种形式(while (goats))才是C程序员最常用的。要想成为一名C程序员,应该多熟悉while (goats)这种形式
🎈(二)for语句实现循环
while循环是不确定循环(indefinite loop)。所谓不确定循环,指在测试表达式为假之前,预先不知道要执行多少次循环。另外,还有一类是计数循环(counting loop)。这类循环在执行循环之前就知道要重复执行多少次,即for循环
在创建一个重复执行固定次数的循环中涉及了3个行为:
- 必须初始化计数器;
- 计数器与有限的值作比较;
- 每次循环时递增计数器。
while循环的测试条件执行比较,递增运算符执行递增,递增一般放在循环的末尾,这可以防止不小心漏掉递增。因此,这样做比将测试和更新组合放在一起(即使用count++ <= NUMBER)要好,但是计数器的初始化放在循环外,就有可能忘记初始化。
for语句的一般形式为
for(表达式1;表达式2;表达式3) {
语句
}
for (initialize; test; update ) {
statement
}
- 表达式1:设置初始条件,只会在for循环开始时执行一次。可以为零个、一个或多个变量设置初值(如i=1)。
- 表达式2:是循环条件表达式,用来判定是否继续循环。在每次执行循环体前先执行此表达式,决定是否继续执行循环。
- 表达式3:作为循环的调整,例如使循环变量增值,它是在执行完循环体后才进行的。
- initialize表达式在执行for语句之前只执行一次;然后对test表达式求值,如果表达式为真 (或非零),执行循环一次;接着对update表达式求值,并再次检查test表达式。for语句是一种入口条件循环,即在执行循环之前就决定了是否执行循环。因此,for循环可能一次都不执行。statement部分可以是一条简单语句或复合语句。
for循环的灵活性:
常见最多的情况是第1个表达式给计数器赋初值,第2个表达式表示计数器的范围,第3个表达式递增计数器。这样使用for循环确实很像其他语言的循环。除此之外,for循环还有其他各种用法。
1.可以使用递减运算符来递减计数器
for (secs = 5; secs > 0; secs--){
....
}
2.可以让计数器递增2、10等
for (n = 2; n < 60; n = n + 13){
...
}
3.可以用字符代替数字计数
for (ch = 'a'; ch <= 'z'; ch++){
...
}
//程序能正常运行是因为字符在内部是以整数形式储存的,因此该循环实际上仍是用整数来计数。
4.第1个表达式不一定是给变量赋初值,也可以使用printf()。
for (printf("Keep entering numbers!\n"); num != 6;){
...
}
5.其它:
/*循环体中的行为可以改变循环头中的表达式,如果程序经过几次迭代后发现delta太小或太大,可以在循环体中改变delta的大小*/
for (n = 1; n < 10000; n = n + delta)
/*除了测试迭代次数外,还可以测试其他条件。如果与控制循环次数相比,你更关心限制立方的大小,就可以使用这样的测试条件。*/
for (num = 1; num*num*num <= 216; num++)
/*可以让递增的量几何增长,而不是算术增长。也就是说,每次都乘上而不是加上一个固定的量:*/
for (debt = 100.0; debt < 150.0; debt = debt * 1.1)
/*第3个表达式可以使用任意合法的表达式。无论是什么表达式,每次迭代都会更新该表达式的值*/
for (x = 1; y <= 75; y = (++x * 5) + 50)
拓展:
1.表达式 1、表达式 2、表达式 3, 这 3 个表达式的任意一个或多个,都可以省略! 但是其中的“;”不可以省略!
for (; ; ) { 循环体 } //相当于: while (1) { 循环体 } for ( ; test ; ) { 循环体 } //相当于: while ( test ) { 循环体 }
2.在 C89 标准中,表达式 1 不能定义变量,在 C99 标准和 C++中,表达式 1 可以定义变量表达式 1 中定义的变量仅在 for 循环中有效。
3.若省略表达式2且在循环体中没有设置循环结束条件,那么该循环将成为一个死循环
🎈(三)do…while语句实现循环
前面讲的while和for循环都是入口条件循环,除了while语句以外,C语言还提供了do…while语句来实现循环结构,它是一种出口条件循环。即在循环的每次迭代之后再检查测试条件,这保证了至少执行循环体中的内容一次。
do…while语句的一般形式为
do
语句
while (表达式) ;
其中的“语句”就是循环体。它的执行过程可以用图5.4表示。请注意do…while循环用N-S流程图的表示形式(图5.4(b))。先执行一次指定的循环体语句,后判别表达式,当表达式的值为非零(“真”)时,返回重新执行循环体语句,如此反复,直到表达式的值等于0(“假”)为止时循环结束
拓展:
特殊用法:
do { // 循环体 } while(0)
⛳二、while和do…while循环的比较
1.用while循环:
# include < stdio. h>
int main( )
{
int i,sum=0;
printf(" please enter i,i=?");
scannf("%d",&i);
while(i<= 10)
{
sum= sum+i;
i++;
}
printf("sum=%d\n",sum);
return 0;
}
运行结果(两次):
please enter i,i=?1
sum = 55
please enter i,i=?11
sum = 0
2.用do…while循环:
# include < stdio. h>
int main( )
{
int i,sum= 0;
printf(' please enter i,i=?");
scanf("%d" ,&i);
do
{
sum = sum+i;
i++;
}while(i< = 10);
printf("sum=%d\n",sum);
return 0;
}
运行结果(两次):
please enter i,i=?1
sum = 55
please enter i,i=?11
sum = 11
可以看到,当输入i的值小于或等于10时,二者得到的结果相同;而当i>10时,二者结果就不同了。这是因为此时对while循环来说,一次也不执行循环体(表达式“i<=10”的值为假),而对d…while循环语句来说则至少要执行一次循环体。可以得到结论:当while后面的表达式的第1次的值为“真”时,两种循环得到的结果相同;否则,二者结果不相同(指二者具有相同的循环体的情况)。
⛳三、循环中的特殊控制:continue 与 break
break :结束本层循环。 如果break语句位于嵌套循环内,它只会影响包含它的当前循环。
continue :结束本次循环,进入下一次循环,如果continue语句在嵌套循环内,则只会影响包含该语句的内层循环。
continue还可用作占位符。例如,在while循环中提到的循环体是空语句采用单独的分号的时候,这种情况一般很难注意到,如果使用continue,可读性会更高
while (getchar() != '\n') continue;
for 语句中的 continue
while 语句中的 continue
⛳四、循环嵌套
嵌套循环(nested loop)指在一个循环内包含另一个循环。嵌套循环常用于按行和列显示数据,也就是说,一个循环处理一行中的所有列,另一个循环处理所有的行。
要点: 把内层的循环,看成一个完整的“大语句”,嵌套循环中的内层循环在每次外层循环迭代时都执行完所有的循环
#include <stdio.h>
// 一天想我几次?
// 24 小时,每秒一次
int main(void) {
int count = 0;
for (int i=0; i < 24; i++) {
for (int j=0; j<60; j++) {
for (int k=0; k<60; k++) {
count++;
printf("%d:%d:%d想你第%d次\n",i,j,k,count);
Sleep(1000);
}
}
}
printf("一天想你%d次\n",count);
return 0;
}
⛳五、如何选择循环
-
首先,确定是需要入口条件循环还是出口条件循环。通常,入口条件循环用得比较多,有几个原因。其一,一般原则是在执行循环之前测试条件比较好。其二,测试放在循环的开头,程序的可读性更高。另外,在许多应用中,要求在一开始不满足测试条件时就直接跳过整个循环。
那么,假设需要一个入口条件循环,用for循环还是while循环?这取决于个人喜好,因为二者皆可。
要让for循环看起来像while循环,可以省略第 1 个和第3个表达式。例如:
for ( ; test ; ) //与下面的while效果相同: while ( test )
要让while循环看起来像for循环,可以在while循环的前面初始化变量, 并在while循环体中包含更新语句。例如:
初始化; while ( 测试 ) { 其他语句 更新语句 } //与下面的for循环效果相同: for ( 初始化 ;测试 ; 更新 ) 其他语句
-
一般而言,当循环涉及初始化和更新变量时,用for循环比较合适,而在其他情况下用while循环更好。
-
对于涉及索引计数的循环,用for循环更适合