创造不易,友友们给个三连吧!!
C语⾔是结构化的程序设计语⾔,这⾥的结构指的是顺序结构、选择结构、循环结构,C语⾔是能够实 现这三种结构的,其实我们如果仔细分析,我们⽇常所⻅的事情都可以拆分为这三种结构或者这三种结构的组合。
我们可以使⽤ if 、 switch 实现分⽀结构,使⽤ for 、 while 、 do while 实现循环结构。
一、if语句
1.1 if
if ( 表达式 )
语句
在C语⾔中,0为假,非0表⽰真,也就是表达式的结果如果是0,则语句不执⾏,表达式的结果如果是 不是0,则语句执⾏。
使用举例:输⼊⼀个整数,判断是否为奇数
int main()
{
int num = 0;
scanf("%d", &num);
if(num % 2 == 1)
printf("%d 是奇数\n", num);
return 0;
}
1.2 else
如果⼀个数不是奇数,那就是偶数了,这⾥就需要 if...else... 语句了
if ( 表达式 )
语句1
else
语句2
使用举例:输⼊⼀个整数,判断是否为奇数,如果是奇数打印是奇数,否则打印数偶数。
int main()
{
int num = 0;
scanf("%d", &num);
if(num % 2 == 1)
printf("%d 是奇数\n", num);
else
printf("%d 是偶数\n", num);
return 0;
}
1.3 分支中包含多条语句
默认在 if 和 else 语句中默认都只控制⼀条语句,那如果我们要if语句同时控制2条语句,怎么办呢?那就要使⽤ { } 将代码括起来, else 后也可以跟上⼤括号。方式如下:
int main()
{
int age = 0;
scanf("%d", &age);
if(age >= 18) //if 后使⽤{} 控制多条语句-这个块也叫:程序块,或者复合语句
{
printf("成年了\n");
printf("可以交⼥朋友了\n");
}
else //else 后使⽤{}控制多条语句-这个块也叫:程序块,或者复合语句
{
printf("未成年\n");
printf("不可以早恋哦\n");
}
return 0;
}
注:使⽤{ }控制多条语句-这个块也叫:程序块,或者复合语句
1.4 嵌套if
在 if else 语句中, else 可以与另⼀个 if 语句连⽤,构成多重判断。
使用举例:要求输⼊⼀个整数,判断输⼊的整数是0,还是正数或者负数。
int main()
{
int num = 0;
scanf("%d", &num);
if(num == 0)
printf("输⼊的数字是0\n");
else if(num > 0) //这⾥的if 相当于嵌套在els语句中,形成了嵌套结构
printf("输⼊的数字是正数\n");
else
printf("输⼊的数字是负数\n");
return 0;
}
1.5 悬空else
如果有多个 if 和 else ,可以记住这样⼀条规则, else 总是跟最接近的 if 匹配。
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
我们发现运行代码后啥也不输出,因为实际上 else 是和第⼆个 if 进⾏匹配的,这样后边的 if...else 语句是嵌套在第⼀个 if 语句中的,如果第⼀个 if 语句就不 成⽴,嵌套 if 和 else 就没机会执⾏了,最终啥都不打印。
如果我们希望else和第⼀个if匹配,可以这样修改代码:
int main()
{
int a = 0;
int b = 2;
if(a == 1)
{
if(b == 2)
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
这说明了:某些情况下带上适当的⼤括号,代码的逻辑就会更加的清晰,代码的可读性会更高!!
二、switch语句
除了 if 语句外,C语⾔还提供了 switch 语句来实现分⽀结构。
switch 语句是⼀种特殊形式的 if...else 结构,⽤于判断条件有多个结果的情况。它把多重 的 else if 改成更易用、可读性更好的形式。
switch (expression) {
case value1:
statement1;
break;
case value2:
statement2;
break;
………………
default: statement
break;
}
上⾯代码中,根据表达式 expression 不同的值,执行相应的 case 分支中的statement语句。如果找不到对应的值, 就执行default 分支中的statement语句。
易错点:
1、switch 后的 expression 必须是整型表达式(char类型也是可以的,因为字符是以ASCII码值的形式存储的,ASCII值是整数,字符属于整型家族)
int main()
{
char ch = ‘w’;
scanf("%c", &ch);
switch(ch)//不会报错
{
case ‘a’:
……
break;
case ‘b’:
……
break;
case ‘c’:
……
break;
}
return 0;
}
2、case 后的值,必须是整形常量表达式
3、case 和后边的value之间必须有空格
4、每⼀个 case 语句中的代码执行完成后,需要加上 break ,才能跳出这个switch语句。
2.1 if和switch的对比
例如:输⼊任意⼀个整数值,计算除3之后的余数
用if语句写:
int main()
{
int n = 0;
scanf("%d", &n);
if(n%3 == 0)
printf("整除,余数为0\n");
else if(n%3 == 1)
printf("余数是1\n");
else
printf("余数是2\n");
return 0;
}
用switch语句写:
int main()
{
int n = 0;
scanf("%d", &n);
switch(n%3)
{
case 0:
printf("整除,余数为0\n");
break;
case 1:
printf("余数是1\n");
break;
case 2:
printf("余数是2\n");
break;
}
return 0;
}
2.2 switch中的break
前⾯的代码中,如果我们去掉case语句中的break,会出现什么情况呢?
int main()
{
int n = 0;
scanf("%d", &n);
switch(n%3)
{
case 0:
printf("整除,余数为0\n");
case 1:
printf("余数是1\n");
case 2:
printf("余数是2\n");
}
return 0;
}
输入:7 输出 :余数是1 余数是2
原因是 switch 语句也是分⽀效果的,只有在 switch 语句中使⽤ break 才能在跳出 switch 语 句,如果某⼀个 case 语句的后边没有 break 语句,代码会继续玩下执行,按顺序执行其他 case 语句中的代码,直到遇到 break 语句或者 switch 语句结束。就⽐如上⾯的代码就执⾏了 case 2 中的语句。
有些时候我们也可以利用这个特点减小代码量,比如:
题目要求:输⼊1-5,输出的是“⼯作⽇”;输⼊6-7,输出“休息⽇”
int main()
{
int day = 0;
scanf("%d", &day);
switch(day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("⼯作⽇\n");
break;
case 6:
case 7:
printf("休息⽇\n");
break;
}
return 0;
}
2.3 switch中的default
在使⽤ switch 语句的时候,我们经常可能遇到⼀种情况,⽐如 switch 后的表达式中的值⽆法匹配代码中的 case 语句的时候,这时候要不就不做处理,要不就得在 switch 语句中加⼊ default ⼦句。
就⽐如前⾯做的打印星期的练习,如果 day 的输⼊不是1~7的值,如果我们要提⽰:输⼊错误
int main()
{
int day = 0;
scanf("%d", &day);
switch(day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("⼯作⽇\n");
break;
case 6:
case 7:
printf("休息⽇\n");
break;
default:
printf("输⼊错误\n");
break;
}
return 0;
}
2.4 switch语句中的case和default的顺序问题
在 switch 语句中 case 语句和 default 语句是没有顺序要求的,只要你的顺序是满⾜实际需求的就可以。
不过我们通常是把 default子句放在最后处理的。
三、while循环
C语⾔提供了3种循环语句, while 就是其中⼀种。 while 语句的语法结构和 if 语句⾮常相似。
while(表达式)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
3.1 while和if对比
//代码1
#include <stdio.h>
int main()
{
if(1)
printf("hehe\n"); //if后边条件满⾜,打印⼀次hehe
return 0;
}
打印一次hehe
int main()
{
while(1)
printf("hehe\n"); //while后边的条件满⾜,死循环的打印hehe
return 0;
}
循环打印hehe
while语句只要后面的条件满足,可以实现循环执行语句!!
首先上来就是执行判断表达式,表达式的值为0,循环直接结束;表达式的值不为0,则执行循环语 句,语句执⾏完后再继续判断,是否进行下⼀次判断。
3.2 while循环的正确使用
因为while循环只要后面的条件满足就可以实现死循环,所以我们需要在while循环的里面的语句增加调整部分使得while每次循环都渐渐接近条件不满足的情况,最后退出。
int main()
{
int i = 1;
while(i<=10)
{
printf("%d ", i);
i++;
}
return 0;
}
初始化部分:int i=1
判断部分:i<=10
调整部分:i++
3.3 while循环的练习
题目:输⼊⼀个正的整数,逆序打印这个整数的每⼀位
例如: 输⼊:1234,输出:4 3 2 1
输⼊:521,输出:1 2 5
题⽬解析:
1. 要想得到n的最低位,可以使⽤n%10的运算,得到的余数就是最低位,如:1234%10得到4
2. 要想去掉n的最低位,找出倒数第⼆位,则使⽤ n=n/10 操作就可以去掉最低位的,如:n=1234/10得到123,123相较于1234就去掉了最低位,123%10就得到倒数第⼆位3。
3. 循环1和2两个步骤,在n变成0之前,就能到所有的位。
int main()
{
int n = 0;
scanf("%d", &n);
while(n)
{
printf("%d ", n%10);
n /= 10;
}
return 0;
}
四、for循环
for 循环是三种循环中使⽤最多的
for(表达式1; 表达式2; 表达式3)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
⾸先执⾏ 表达式1初始化循环变量,接下来就是执⾏表达式2的判断部分, 表达式2的结果如果 ==0,则循环结束; 表达式2的结果如果!=0则执⾏循环语句,循环语句执⾏完后,再去执⾏表达式 3 ,调整循环变量,然后再去表达式2 的地⽅执⾏判断, 根据表达式2的结果是否为0,决定循环是否继续。
整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。
4.1 for循环的使用
在屏幕上打印1~10的值
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0;
}
4.2 for循环和while循环的对比
for 和 while 在实现循环的过程中都有初始化、判断、调整这三个部分,但是 for 循环的三个部 分非常集中,便于代码的维护,⽽如果代码较多的时候 while 循环的三个部分就⽐较分散,所以从形 式上 for 循环要更优⼀些。
注:for循环的初始化部分,判断部分,调整部分都可以省略,但是要注意判断部分省略了表示判断部分恒为真,会无限循环下去。
五、do-while循环
在循环语句中 do while 语句的使⽤最少
do
语句;//想包含更多的语句,可以加上⼤括号
while(表达式);
while 和 for 这两种循环都是先判断,条件如果满⾜就进⼊循环,执⾏循环语句,如果不满⾜就跳 出循环;
⽽ do while 循环则是先直接进⼊循环体,执⾏循环语句,然后再执⾏ while 后的判断表达式,表 达式为真,就会进⾏下⼀次,表达式为假,则不再继续循环。
在 do while 循环中先执⾏图上的“语句”,执⾏完语句,在去执⾏“判断表达式”,判断表达式的 结果是!=0,则继续循环,执⾏循环语句;判断表达式的结果==0,则循环结束。
所以在 do while 语句中循环体是⾄少执行⼀次的,这是 do while 循环比较特殊的地⽅。
练习:输⼊⼀个正整数,计算这个整数是⼏位数?
例如: 输⼊:1234 输出:4
输⼊:12 输出:2
int main()
{
int n = 0;
scanf("%d", &n);
int cnt = 0;
do
{
cnt++;
n = n / 10;
} while (n);
printf("%d\n", cnt);
return 0;
}
这⾥并⾮必须使⽤ do while 语句,但是这个代码就⽐较适合使⽤ do while 循环,因为n即使是 0,也是1位数,要统计位数的。
六、break和continue语句
在循环执⾏的过程中,如果某些状况发⽣的时候,需要提前终止循环,这是⾮常常⻅的现象。C语⾔中 提供了 break 和 continue 两个关键字,就是应该到循环中的。
break 的作⽤是⽤于永久的终止循环,只要 break 被执⾏,直接就会跳出循环,继续往后执⾏。
continue 的作⽤是跳过本次循环 continue 后边的代码,在 for 循环和 while 循环中有所差异的。
6.1 while和do while循环中的break和continue
break:
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
break;//当i等于5后,就执⾏break,循环就终⽌了
printf("%d ", i);
i = i+1;
}
return 0;
}
运行结果:1 2 3 4 因为打印了1,2,3,4后,当i等于5的时候,循环正 break 的地⽅终⽌,不再打印,不再循环。
所以 break 的作⽤就是永久的终⽌循环,只要 break 被执⾏, break 外的第⼀层循环就终⽌了。 那以后我们在循环中,想在某种条件下终⽌循环,则可以使⽤ break 来完成我们想要的效果。
continue:
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
continue;
//当i等于5后,就执⾏continue,直接跳过continue的代码,去循环的判断的地⽅
//因为这⾥跳过了i = i+1,所以i⼀直为5,程序陷⼊和死循环
printf("%d ", i);
i = i+1;
}
return 0;
}
程序会死循环 因为 continue 可以帮助我们跳过某⼀次循环 continue 后边的代码,直接到循环的判断部分,进⾏下⼀次循环的判断,如果循环的调整是在 continue 后边的话,可能会造成死循环。
所以continue 是继续的意思,在循环中的作⽤就是跳过本次循环中 continue 后边的代码,继续进⾏ 下⼀次循环的判断。
do.while 语句中的 break 和 continue 的作⽤和 while 循环中⼏乎⼀模⼀样,所以不做讲解。
6.2 for循环中的break和continue
int main()
{
int i = 1;
for(i=1; i<=10; i++)
{
if(i == 5)
break;
printf("%d ", i);
}
return 0;
}
运行结果:1 2 3 4 因为打印了1,2,3,4后,当i等于5的时候,循环正 break 的地⽅终⽌,不再打印,不再循环。
这也说明的break在循环结构里面作用是一致的,就是 永久的终⽌循环
int main()
{
int i = 1;
for(i=1; i<=10; i++)
{
if(i == 5)
continue;//这⾥continue跳过了后边的打印,来到了i++的调整部分
printf("%d ", i);
}
return 0;
}
运行结果:1 2 3 4 6 7 8 9 10 因为这⾥continue跳过了后边的打印,来到了i++的调整部分 在 for 循环中
continue 的作⽤是跳过本次循环中 continue 后的代码,直接去到循环的调 整部分。未来当某个条件发⽣的时候,本次循环⽆需再执⾏后续某些操作的时候,就可以使⽤ continue 来实现。
这也说明了continue在for和while循环中略有差异,对于for循环来说,因为调整部分是写在上面的,所以无论如何都不能跳过调整部分,最多就是少执行一次,而对于while循环来说,如果调整部分在continue后面,那么必然会造成死循环!!
七、循环的嵌套
三种循环 while , do while , for ,这三种循环往往会嵌套在⼀起才能更好的解决 问题,就是我们所说的:循环嵌套
题目:找出100~200之间的素数,并打印在屏幕上。
题目解析:
1. 要从100~200之间找出素数,⾸先得有100~200之间的数,这⾥可以使⽤循环解决。
2. 假设要判断i是否为素数,需要拿2~i-1之间的数字去试除i,需要产⽣2~i-1之间的数字,也可以使⽤循环解决。(嵌套循环)
3. 如果2~i-1之间有数字能整除i,则i不是素数,如果都不能整除,则i是素数。(设置一个flag作为打印标志)
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=100; i<=200; i++)
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<i; j++)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}
注:n=a*b。假设n是16,a=8则b=2,a=4则b=4,所以我们发现一个整数如果想要拆分成两个因子想乘的话,那么这两个因子至少有一个会小于等于根号n,所以判断n是否是质数,不需要遍历 2——n-1的数字了,只要遍历2——根号n的数字就可以了,这样运行效率就会更高!!!
优化版:
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=100; i<=200; i++)
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<=sqrt(i); j++)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}
sqrt是一个开平方的库函数,头文件是<math.h>
八、goto语句
C语⾔提供了⼀种⾮常特别的语法,就是 goto 语句和跳转标号, goto 语句可以实现在同⼀个函数 内跳转到设置好的标号处。
int main()
{
printf("hehe\n");
goto next:
printf("haha\n");
next:
printf("跳过了haha的打印\n");
return 0;
}
goto 语句如果使用的不当,就会导致在函数内部随意乱跳转,打乱程序的执⾏流程,所以我们的建议是能不⽤尽量不去使⽤;
但是 goto 语句也不是⼀⽆是处,在多层循环的代码中,如果想快速跳出 使⽤ goto 就非常的方便了。
for(...)
{
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
}
error:
//...
本来 for 循环想提前退出得使⽤ break ,⼀个 break 只能跳出⼀层 for 循环,如果3层循环嵌套 就得使⽤3个 break 才能跳出循环,所以在这种情况下我们使⽤ goto 语句就会更加的快捷。
九、随机数生成方法
9.1 rand
C语⾔提供了⼀个函数叫 rand,这函数是可以⽣成随机数的
int rand (void);
rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的⼤⼩是 依赖编译器上实现的,但是⼤部分编译器上是32767。
rand函数的使⽤需要包含⼀个头⽂件是:stdlib.h
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
运行结果:41 18467 6334 26500 19169
注意:虽然⼀次运⾏中产⽣的5个数字是相对随机的,但是下⼀次运⾏程序⽣成的结果和上⼀次⼀模⼀样,因为rand函数⽣成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法⽣成的随机数。真正的随机数的是⽆法预测下⼀个值是多少的。⽽rand函数是对⼀个叫“种⼦”的基准值进⾏运算⽣成的随机数。
之所以前⾯每次运⾏程序产⽣的随机数序列是⼀样的,那是因为rand函数⽣成随机数的默认种⼦是1。 如果要⽣成不同的随机数,就要让种⼦是变化的。
9.2 srand
C语⾔中⼜提供了⼀个函数叫 srand,⽤来初始化随机数的⽣成器的
程序中在调⽤ rand 函数之前先调⽤ srand 函数,通过 srand 函数的参数seed来设置rand函数⽣成随 机数的时候的种⼦,只要种⼦在变化,每次⽣成的随机数序列也就变化起来了。
那也就是说给srand的种⼦是如果是随机的,rand就能⽣成随机数;在⽣成随机数的时候⼜需要⼀个随机数,这就⽭盾了!因此我们需要用到time!!
9.3 time
在程序中我们⼀般是使⽤程序运⾏的时间作为种⼦的,因为时间时刻在发生变化的。
在C语⾔中有⼀个函数叫 time ,就可以获得这个时间
time_t time (time_t* timer);
time 函数会返回当前的⽇历时间,其实返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的差值,单位是秒。返回的类型是time_t类型的,time_t 类型本质上其实就是32位或者64位的整型类型(和size_t一样,在64位机器是8个字节,在32位机器是4个字节)。
使用注意事项:
1、time函数的参数 timer 如果是⾮NULL的指针的话,函数也会将这个返回的差值放在timer指向的内存中带回去。
2、如果 timer 是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做:时间戳。
3、time函数的时候需要包含头⽂件:time.h
4、时间戳是不断变化的,所以我们将time函数的返回值作为种子。
所以随机数生成代码可以这样写:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
//使⽤time函数的返回值设置种⼦
//因为srand的参数是unsigned int类型,我们将time函数的返回值强制类型转换
srand((unsigned int)time(NULL));//srand的参数类型是unsigned int,所以要强转
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
注意:srand函数是不需要频繁调⽤的,⼀次运⾏的程序中调⽤⼀次就够了。 (所以我们一般把他放在main函数里面的靠前位置),如果每生成一次随机数之前就调用一次srand函数,那么得到随机数会非常地接近。
9.4 设置随机数的范围
⽣成0~99之间的随机数的⽅法
rand() %100;//余数的范围是0~99
要⽣成100~200的随机数的⽅法
100 + rand()%(200-100+1)
//余数的范围是0~100,加100后就是100~200
要⽣成a~b的随机数的⽅法
a + rand()%(b-a+1)
十、猜数字游戏的实现
写一个猜数字游戏
要求:
1、电脑⾃动⽣成1~100的随机数
2、玩家猜数字,猜数字的过程中,根据猜测数据的⼤⼩给出⼤了或⼩了的反馈,直到猜对,游戏结束
3、如果5次猜不出来,就算失败
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{
int r = rand() % 100 + 1;
int guess = 0;
int count = 5;
while (count)
{
printf("\n你还有%d次机会\n", count);
printf("请猜数字>:");
scanf("%d", &guess);
if (guess < r)
{
printf("猜⼩了\n");
}
else if (guess > r)
{
printf("猜⼤了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
count--;
}
if (count == 0)
{
printf("你失败了,正确值是:%d\n", r);
}
}
void menu()
{
printf("***********************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("***********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戏结束\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}