03.【C语言学习笔记】分支和循环
目录
1. else悬空问题
2. 关系操作符(比较运算符)
3. 条件操作符(三目操作符)
4. 逗号表达式
5. 逻辑操作符(!、&&、||)、短路解释
6. switch语句(switch、case、break、default)
7. cuntinue 在 for 循环中的注意
8. goto语句跳转
9. 随机数的生成(rand、srand、time)
9.1 rand
9.2 srand
9.3 time
9.4 设置随机数范围
C语言是结构化的程序设计语言,这里的结构指的是顺序结构、选择结构、循环结构。我们可以使用 if 、switch 实现分支结构,使用 for 、while 、do while 实现循环结构。
1. else悬空问题
如果有多个if 和else ,可以记住这样一条规则, else 总是跟最接近的if 匹配。我们首先从下面代码开始。以下代码什么也不输出,因为else是和第二个 if 匹配的,这样后边的if...else 语句是嵌套在第一个if 语句中的,如果第一个if 语句就不成立,嵌套if 和else 就没机会执行了,最终啥都不打印。
#include <stdio.h>
int main()
{int a = 0;int b = 2;if (a == 1)if (b == 2)printf("hehe\n");elseprintf("haha\n");return 0;
}// 上下两个代码的功能一样#include <stdio.h>
int main()
{int a = 0;int b = 2;if (a == 1){if (b == 2)printf("hehe\n");elseprintf("haha\n");}return 0;
}
2. 关系操作符(比较运算符)
C 语言用于比较的表达式,称为 “关系表达式”(relational expression),里面使用的运算符就称为“关系运算符”(relational operator),主要有下面6个。
• > 大于运算符
• < 小于运算符
• >= 大于等于运算符
• <= 小于等于运算符
• == 相等运算符
• != 不相等运算符
注意:多个关系运算符不宜连用。
3. 条件操作符(三目操作符)
exp1 ? exp2 : exp3
如果exp1 为真, exp2 计算,计算的结果是整个表达式的结果;如果exp1 为假, exp3 计算,计算的结果是整个表达式的结果。
4. 逗号表达式
#include <stdio.h>int main()
{int b = 0;int c = 4;int a = (a=3, a+3, b=c*3, c=2*a+b)// a=3, b=12 c=18// 逗号表达式,会从左向右依次计算,但是整个表达式的结果是最后一个表达式的结果printf("%d\n", c); // 18printf("%d\n", a); // 18return 0;
}
5. 逻辑操作符(!、&&、||)、短路解释
逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。
• ! :逻辑取反运算符(改变单个表达式的真假)。单目操作符
• && :与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。双目操作符
• || :或运算符,就是或者的意思(两侧有一个表达式为真,则为真,否则为假)。双目操作符
注:C语言中,非0表示真,0表示假
短路:C语言逻辑运算符还有一个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
6. switch语句(switch、case、break、default)
switch (expression) {case value1: statementcase value2: statementdefault: statement
}
注:
• switch 后的expression 必须是整型表达式
• case 后的值,必须是整形常量表达式
• case 和后边的数字之间必须有空格
• 每一个case 语句中的代码执行完成后,需要加上break ,才能跳出这个switch语句。
• case 语句和 default 语句是没有顺序要求的,只要你的顺序是满足实际需求的就可以。
7. cuntinue 在 for 循环中的注意
for (1 表达式1; 表达式2; 表达式3)
// 表达式1 用于循环变量的初始化
// 表达式2 用于循环结束条件的判断
// 表达式3 用于循环变量的调整
for 循环中 continue 执行后,再执行表达式3的循环变量调整,再进行下一次循环,下述代码不会出现死循环。
#include <stdio.h>
int main()
{int i = 1;for (i = 1; i <= 10; i++){if (i == 5)continue;//这里continue跳过了后边的打印,来到了i++的调整部分printf("%d ", i);}return 0;
}
8. goto语句跳转
goto 语句和跳转标号
#include <stdio.h>
int main()
{printf("hehe\n");goto next:printf("haha\n");
next:printf("跳过了haha的打印\n");return 0;
}
9. 随机数的生成(rand、srand、time)
9.1 rand
int rand (void);
rand函数会返回一个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的大小是依赖编译器上实现的,但是大部分编译器上是32767。
rand函数的使用需要包含一个头文件是:stdlib.h
伪随机数不是真正的随机数,是通过某种算法生成的随机数。真正的随机数的是无法预测下一个值是多少的。而rand函数是对一个叫“种子”的基准值进行运算生成的随机数。
之所以前面每次运行程序产生的随机数序列是一样的,那是因为rand函数生成随机数的默认种子是1。如果要生成不同的随机数,就要让种子是变化的。
9.2 srand
srand,用来初始化随机数的生成器。
void srand(unsigned int seed);
程序中在调用 rand 函数之前先调用 srand 函数,通过 srand 函数的参数seed来设置rand函数生成随机数的时候的种子,只要种子在变化,每次生成的随机数序列也就变化起来了。
那也就是说给srand的种子是如果是随机的,rand就能生成随机数;在生成随机数的时候又需要一个随机数,这就矛盾了。
一般用程序运行的时间作为种子。
9.3 time
time_t time(time_t* timer);
time 函数返回的是 时间戳。
• time 函数会返回当前的日历时间,其实返回的是1970年1月1日0时0分0秒到现在程序运行时间之间的差值(时间戳),单位是秒。返回的类型是time_t类型的,time_t 类型本质上其实就是32位或者64位的整型类型。
• time函数的参数 timer 如果是非NULL的指针的话,函数也会将这个返回的差值放在timer指向的内存中带回去。(指针传参)
• 如果 timer 是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做:时间戳。time函数的时候需要包含头文件:time.h
如果只是让time函数返回时间戳,就可以这样写。
time(NULL);//调用time1 函数返回时间戳,这里没有接收返回值
生成随机数的代码改写成如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{//使用time函数的返回值设置种子//因为srand的参数是unsigned int类型,我们将time函数的返回值强制类型转换srand((unsigned int)time(NULL));printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}
9.4 设置随机数范围
生成0~99之间的随机数
rand() % 100; // 余数的范围是0~99
生成1~100之间的随机数
rand() % 100 + 1;//%100的余数是0~99,0~99的数字+1,范围是1~100
生成100~200之间的随机数
100 + rand() % (200 - 100 + 1) //余数的范围是0~100,加100后就是100~200
生成a~b之间的随机数
a + rand()%(b-a+1)