目录
1.关系操作符
C语言用于比较的的表达式,称为“关系表达式”,其中使用的运算符就叫做“关系运算符”,主要有以下六个:
- > 大于运算符
- < 小于运算符
- <= 小于等于运算符
- >= 大于等于运算符
- == 相等运算符
- != 不相等运算符
如下列举例
a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;
关系表达式通常返回0来表示假,返回非零表示真。如9>8返回1,8>9返回0。
关系表达式常用于if或while结构。
if(a==9)
{
printf("%d is baka",a);
}
注意:相等运算符==和赋值运算符=是两个不一样的运算符,如果混淆可能会写出错误但是能运行,以至于不好发现错误的代码。
if(a=3)
{
//...
}
上面的代码,原意是a==3,但是不小心写成了a=3,这个式子表示对变量a赋值3,返回值为3,所以if的判断总是为真。
为了防止这种错误的出现,咱们可以调换一下变量的位置,如下
if(3==a)
{
//...
}
如果这时候误把==写成了=,那么编译器就会报错,因为3是一个常数,不能给一个常数赋值。
注意:还有一个需要避免的错误:多个关系运算符不宜连用。
i<j<k
上面示例中,连续使用两个运算符,确实合法,不会报错,但是达不到想要的效果——j不一定在i和k的值之间——因为关系运算符从左向右计算,所以执行实际如下,
(i<j)<k
i<j先一步返回0或者1,所以最终是0或1来和k进行比较。如果想要判断变量j在不在i和k之间,可以有以下写法。
//&&是逻辑运算符,下面会讲
i<j && j<k
举个例子,通过年龄判断一个人是不是大学生
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (18 <= age <= 22)
{
printf("大概率是大学生\n");
}
return 0;
}
如果按以上写法,当输入为10的时候,如下

输出仍为“大概率是大学生",就是因为先判断了18<=age返回了0,于是有0<=22的判断,返回了1,为真所以进入if语句进行打印。
所以咱们应该把条件修改为如下
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (18 <= age && age <= 22)
{
printf("大概率是大学生\n");
}
return 0;
}
剩下的操作符同理,按字面意思使用即可。
2.条件操作符
条件操作符也叫做三目操作符,需要接受3个操作数,如下
exp1 ? exp2 : exp3
条件操作符的计算逻辑是:如果 exp1 为真, exp2 计算,计算的结果是整个表达式的结果;如果 exp1 为假, exp3 计算,计算的结果是整个表达式的结果。
也就是说条件操作符能在一定程度上代替if语句,从而使代码简洁。
#include <stdio.h>
int main()
{
size_t a;
scanf("%zd", &a);
if (a > 0)
{
a *= 2;
}
else
{
a = 4;
}
printf("%zd", a);
return 0;
}
如上代码,输入一个a,如果a为0则赋值为4,否则a变为原来的两倍。现在,把上面的代码用三目运算符来实现。如下
#include <stdio.h>
int main()
{
size_t a;
scanf("%zd", &a);
a = a == 0 ? 4 : 2 * a;
printf("%zd", a);
return 0;
}
再比如,找到两个数之间的较大值,如下,这种时候使用看起来比if语句少很多行。
#include <stdio.h>
int main()
{
int a = 9;
int b = 10;
int max;
max = a > b ? a : b;
return 0;
}
3.逻辑操作符
逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有以下三个运算符
- ! 逻辑取反运算符(改变单个表达式的真假)
- && 逻辑与运算符,就是并且的意思(两侧都为真则表达式为真,否则为假)
- || 逻辑或运算符,就是或者的意思(两侧有一个真则表达式为真,否则为假)
注:C语言中,非0表示真,0表示假
3.1逻辑取反操作符!
| a | !a |
| 非0 | 0 |
| 0 | 1 |
比如,咱们有个变量叫flag,如果flag为假,就要做点什么,那么就可以这么写
#include <stdio.h>
int main()
{
int flag = 0;
if (!flag)
{
printf("do something that is dull");
}
return 0;
}
如果flag为真,那么!flag为假,如果flag为假,!flag为真,所以上面的代码的意思就是flag为假,执行if语句中的代码。
3.2与运算符
| a | b | a&&b |
| 非0 | 非0 | 1 |
| 非0 | 0 | 0 |
| 0 | 非0 | 0 |
| 0 | 0 | 0 |
&&为与运算符,也就是并且的意思,&&是一个双目运算符,使用方式就是a&&b,&&表达式两边表达式都为真时,整个表达式才为真,只要有一个假,那么就是假。
比如,3月到5月是春天,用代码表示就下,
#include <stdio.h>
int main()
{
int month;
scanf("%d", &month);
if (month >= 3 && month <= 5)
{
printf("春天来了!");
}
return 0;
}
这里的表达式意思就是month既要大于等于3也要小于等于5,必须同时满足。
3.3或运算符
| a | b | a||b |
| 非0 | 非0 | 1 |
| 非0 | 0 | 1 |
| 0 | 非0 | 1 |
| 0 | 0 | 0 |
||就是或运算符,也就是或者的意思,||是一个双目运算符,使用方法是a||b,||两边的表达式只要有一个是真,整个表达式就是真,两边表达式为假的时候,才为假。
比如,用代码来表现,一年中月份是12月或者1月或者2月是冬天,
#include <stdio.h>
int main()
{
int month;
scanf("%d", &month);
if (month==12||month==1||month==2)
{
printf("冬天!");
}
return 0;
}
3.4闰年的判断
现要求,输入应该年份year,判断year是否为闰年。
闰年的判断规则(两条规则满足其一即可):
- 能被4整除并且不能被100整除
- 能被400整除
#include <stdio.h>
int main()
{
int month;
scanf("%d", &month);
if (month % 400 == 0 || (month % 4 == 0 && month % 100 != 0))
{
printf("是闰年\n");
}
else
{
printf("不是闰年\n");
}
return 0;
}
4.5短路
C语言逻辑运算符还有一个特点,它总是对左侧的表达式求值,再对有点的表达式求值,这个顺序是保证的。
如果左边的代码表达式满足逻辑运算符的条件,就不再对右边的表达式求值,这种情况就称之为“短路”。
对于前面的代码
if(month >= 3 && month <= 5)
左操作数为month>=3,有操作数为month<=5,当左操作数的结果是0的时候,即使不判断month<=5,整个表达式的结果也是0。
所以对于&&操作符来说,左边的操作数的结果为0时,右边的操作数就不执行。
那么对于||操作符有事怎样的呢?再来看看前面的代码
if(month == 12 || month==1 || month == 2)
r如果month==12,则不用在判断month是否等于1或2,整个表达式的结果也是1。
所以,||操作符的左操作数的结果不为0时,就无需执行右操作数。
以上这种仅仅根据左操作数的结果就能知道整个表达式的结果,不再对右操作数进行计算的运算称为短路求值。
4.补充:goto语句
C语言提供了一种非常特别的语法,就是goto语句和跳转标号,goto语句可以实现在同一个函数内跳转到设置好的标号处。如下
#include <stdio.h>
int main()
{
printf("cirno\n");
goto lake;
printf("baka");
lake:
printf("skip baka");
return 0;
}
goto语句使用不当的话会导致函数在内部胡乱跳转,打乱程序的执行流程,所以能少用就少用。但是goto也并非一无是处,在多层循环的代码中,如果想要快速跳出,使用goto就非常方便了。如下,
for (...)
{
for (...)
{
for (...)
{
if (disaster)
goto error;
}
}
}
//....
5.猜数字游戏
有了前面的一些知识,就可以开始写一些有趣的代码了,比如写一个猜数字游戏。
游戏要求:
- 电脑自动生成1~100的随机数
- 玩家猜数字,猜数字的过程中,根据猜测的大小给出大了或者小了的反馈,直到猜对为止。
5.1随机数的生成
想要完成猜数字游戏,首先得产生随机数,那么怎么产生随机数呢?
5.1.1rand
C语言提供了一个函数,叫rand,需要包含头文件stdlib.h,这函数是可以生成随机数的,原型如下
int rand (void);
rand函数会返回一个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的大小是依赖编译器上实现的,大部分情况是32767。
来测试一下rand吧,
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
可以看到以下输出,结果很随机了

再运行一次呢?

一模一样耶!其实rand生成的随机数是伪随机,也就不是真正的随机数,而是通过某种算法生成的随机数。真正的随机数是无法预测下一个值是多少的。而rand函数是对一个叫做“种子”的基准值进行运算生成的随机数。
之所以前面每次运行的结果相同吗,是因为rand函数生成随机数的默认种子是1。
所以,想要生成不同的随机数,就是要让种子变化。
5.1.2srand
C语言又提供了一个函数叫srand,同样需要包含头文件stdlib.h。这个函数是用来初始化随机数的生成器,原型如下,
void srand (unsigned int seed);
程序在调用rand之前,会先调用srand函数,通过srand函数的参数seed来设置rand函数生成的随机数时需要的种子,只要种子在变化,每次生成的随机数序列就变化起来了。
也就是说,只要让srand函数的参数是随机的,rand就能生成随机数,但是生成一个随机数就需要一个随机数,不是很矛盾吗?
5.1.3time
在程序中,咱们一般是使用程序运行的时间作为种子的,因为时间时刻在变化。
在C语言中有一个函数,叫time,需要包含头文件time.h,就可以获得这个时间,以下为time原型
time_t time (time_t* timer);
time函数会返回当前的日历时间,其实返回的是1970年1月1日0时0分0秒到现在程序运行时间之间的差值,单位是秒。返回类型的time_t类型的,本质上就是32位或者64位整型类型。
time函数的参数timer如果是非NULL指针的话,函数会将这个返回的差值放在timer指向的内存中带回去。
如果timer是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做时间戳。
这时候就可以改写生成随机数的代码,
int main()
{
//time返回值用于设置种子
//strand接受的是unsigned int类型的参数,所以要强制类型转换
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;
}
再来运行几次,差异这下就出来了。


注:srand函数是不需要频繁调用的,一次运行程序中调用一次就够了。
5.1.4设置随机数的范围
如果要生成0~99之间的随机数,方法如下,
rand() %100;//余数的范围是0~99
如果要生成1~100之间的随机数,方法如下,
rand() %100 + 1;//余数的范围是0~99
如果要生成100~200的随机数,方法如下,
100 + rand()%(200-100+1)//余数范围为0~100
所以,如果要生成a~b的随机数,方法如下
a + rand()%(b-a+1)
5.2猜数字游戏实现
5.2.1设计开始界面
游戏开始,需要有引导来确保输入正确,以至于游戏能够正常开始。
设计的显示开始界面的函数如下
void menu()
{
printf("***********************\n");
printf("***** 1.game ******\n");
printf("***** 2.quit ******\n");
printf("***********************\n");
printf("请输入数字:>");
}
5.2.2让程序先走起来
先不管怎么猜,先让咱们的输入输出有回应,
int main()
{
//设置随机数种子
srand((unsigned int)time(NULL));
//通过输入a来开始
int a = 0;
//打印开始界面
menu();
//正常输入则进入循环
while (scanf("%d", &a) != EOF)
{
switch (a)
{
case 1:
{
printf("游戏开始,请输入一个数字\n");
//这里是还未实现的猜数字游戏函数
}
case 2:
{
printf("游戏结束\n");
break;
}
//出现1和2之外的数据则重新输入
default: printf("输入错误请重新输入\n");
}
//游戏结束
if (a == 2) break;
}
return 0;
}
以上,先打印开始界面,通过用户的输入,来走switch的三种情况,即1开始游戏,2结束游戏,3重新输入。当游戏猜完结束时,再一次对a输入从而进行下一把,或者结束。
上述代码运行如下,

当输入1时,
但是这时候,还没有游戏函数,所以先来重新输入。这次输入2,

再来输入1和2之外的数试试。此时提醒重新输入。

5.2.3猜数字的逻辑
猜数字需要做的,猜在范围内设置好的数字。
范围内的数字就很好设置了
int num = 0;
//数字在1~100
num = 1 + rand() % 100;
那么猜就要靠用户输入了,需要循环来保证持续的输入,需要判断大小来给出合理的提示,猜完之后需要弹出菜单来指引下一步操作。
int cai = 0;
//先让循环走起来,通过break来退出
while (1)
{
//用户输入cai
scanf("%d", &cai);
//cai来和目标num比较,cai大则说明猜大了
if (cai > num)
{
printf("猜大了\n");
}
//cai比num小,说明猜小了
else if (cai < num)
{
printf("猜小了\n");
}
//否则就是猜中了,此时break退出循环
else
{
printf("恭喜你猜对了!\n");
break;
}
}
//游戏结束,弹出菜单来引导继续或者结束
menu();
将两份代码结合成一个函数
void guess()
{
int num = 0;
num = 1 + rand() % 100;
int cai = 0;
while (1)
{
scanf("%d", &cai);
if (cai > num)
{
printf("猜大了\n");
}
else if (cai < num)
{
printf("猜小了\n");
}
else
{
printf("恭喜你猜对了!\n");
break;
}
}
menu();
}
5.2.4最终结果
以下是最终代码
int main()
{
srand((unsigned int)time(NULL));
int a = 0;
menu();
while (scanf("%d", &a) != EOF)
{
switch (a)
{
case 1:
{
printf("游戏开始,请输入一个数字\n");
//参见上方代码
guess();
break;
}
case 2:
{
printf("游戏结束\n");
break;
}
default: printf("输入错误请重新输入\n");
}
if (a == 2) break;
}
return 0;
}
5.2.5拓展:限定次数猜数字
如果想要增加限制,在五次之内猜出数字,该怎么修改代码呢?
其实需要做的只是添加计数,修改循环条件,顺带修改一下输入数字后的提示,以下是对游戏函数的修改。
void guess_count()
{
int num = 0;
num =1+ rand()%100;
int cai = 0;
//增加计数,设定为5
int count = 5;
//循环条件修改为count
while(count)
{
scanf("%d", &cai);
if (cai > num)
{
//每猜一次都--count,并修改游戏提示
--count;
printf("猜大了,还有%d次机会\n", count);
}
else if (cai < num)
{
--count;
printf("猜小了,还有%d次机会\n", count);
}
else
{
printf("恭喜你猜对了!\n");
break;
}
}
//出循环有两个条件:break、count==0,所以要进行判断是否提示
if (count == 0)
{
printf("机会用完了,再试一次吧!\n");
}
menu();
}
最后只需要把上面的函数替换掉主函数中的guess函数就可以了。
2237





