一、操作符的分类
1、算数操作符:+、-、*、/、%、==
2、关系操作符:>、<、>=、<=、!=
3、赋值操作符:=、+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
4、条件操作符: ? :
5、逗号表达式:,
6、逻辑操作符:&&、||、!
7、单目操作符:+、-、!、++、--、*、~、&、|、sizeof、类型转换
8、移位操作符:<<、>>
9、位操作符:&、|、^
10、下标引用操作符:[ ]
11、函数调用操作符:()
12、结构体访问操作符
二、介绍
上我会介绍算术操作符,部分赋值操作符,关系操作符,逻辑操作符,条件操作符,逗号表达式,部分单目操作符
比较进阶的操作符:移位操作符,位操作符、解引用操作符等。
我知道你可能会看的头皮发麻,但是你先别急,这里有许多操作附,学过数学的就,对这些操作符进行用法和逻辑上的了解后即可轻松掌握。列如算数操作符里的,加减乘除,取等,取模运算。
三、算数操作符
+、-、*、/、%、==。
3.1简单表述
这里,乘号(×)和除号(÷)与我们数学里学的不一样,在C语言里乘号使用 * 表示,除号使用 / 表示,用法与数学里的没有区别完全一样。
3.2取模运算
取模运算(%),一般用来求取一个数除以另一个数的余数,在编程中,取模运算经常用于确定一个数除以另一个数后剩余的部分。比如 5 / 2 的商是 2 余数是 1 ,那么 5 % 2 的结果为1。简单了解取模运算的原理后,以下是几个案例。
它的基础写法:
int number = 10 % 3;
number的值就为1。
更进一步考虑,既然取模运算是用来求两数相除后的余数,那一位个位数模10后的结果是多少呢?答案是:个位数本身,因为取模运算在进行十位或更高位计算时能够被整除,余数会是它的个位数。
例如,考虑一个三位数 123:
- 123 除以 10 的商是 12,余数是 3。
- 所以,123 % 10 的结果是 3,即123的个位数。
这个规则适用于任何个位数的数字:
- 如果数字是 56,那么 56 % 10 的结果是 6。
- 如果数字是 789,那么 789 % 10 的结果是 9。
有了进一步对取模运算的了解,我们可以对它进一步拓展,既然十位或更高位数模10后可以取得它的个位,那我们能不能运用取模运算来获取高位数的十位,百位呢?
例如,获取一个百位数:345三位个位数
345 % 10 == 5;
那345模100呢?
345 % 100 == 45;
可以发现这样不断模更高位的10并不能获取我们想要的数,既然这样那我们可不可以取出个位5后将345 除 10 后求出 34 在模 10 就取可以取出第二个数 4 了
345 / 10 == 34;
34 % 10 == 4;
结合起来就是:345 / 10 % 10 == 4;
以此类推,取出百位数时我们可以除100
345 / 100 == 3;
3 % 10 == 3;
345除100直接等于3,在模10就很啰嗦,所以它的写法为:
345 / 100 == 3;
#include <stdio.h>
int main()
{
//int m = 5;
//int n = 3;
//int a = 5 % 3;
//我们该如何将23345拆分位四位个位数
//2 3 4 5
int n = 2345;
int a = 2345 % 10;
int b = 2345 / 10 % 10;
int c = 2345 / 100 % 10;
int d = 2345 / 1000;
printf("%d %d %d %d\n", d, c, b, a);//方便观看我逆序打印
return 0;
}
3.3除号的细节处理
事实上除号( / ),在C语言里还有许多小细节。
int main()
{
int c = 3 / 2;
double d = 3 / 2;
printf("%d %lf", c, d);
return 0;
}
这里c 和 d 的结果分别是多少呢?c == 1,d == 1.5吗?它真的对吗?
诶?是不是很奇怪,为什么我使用浮点数来接收3/2的值为1???别先别急,先看看这样改动后代码运行的结果是什么?
int main()
{
int c = 3 / 2;
double d = 3.0 / 2;
printf("%d %lf", c, d);
return 0;
}
这次,当我在3后面添上一个小数点和0后,打印结果与我们预期相符合。我来简单介绍一下。
因为在C语言规则里有整形除法,浮点型除法。
·整形除法:当两个操作数都是整形时进行整形除法,整数除法的结果是商的整数部分,即除法后的小数部分会被丢弃,例如 3除2的商为1.5但由于执行的是整形除法,去除小数后,所以结果为1。
·浮点数除法:在两个操作数里其中一个操作数为浮点数时,进行浮点数除法,3.0 / 2或3 / 2.0都是执行浮点除法,即两数相除后,保留完整的商,保留小数部分。所以 3.0 / 2的结果为1.5。
四、关系操作符
>、<、>=、<=、!=
关系操作符又称为关系表达式,在C语言里用于比较两个操作数的大小关系,并根据比较的结果返回真和假(布尔值bool)。
>(大于号)、<(小于号)、>=(大于等于)、<=(小于等于)、!=(不等于)
代码演示:
#include <stdio.h>
int main()
{
int a = 10;
int b = 3;
if (a > b)
printf("hehe\n");
else if (a % b > b)
printf("hahaha\n");
else
printf("第二个else if 表达式返回值为假不执行");
return 0;
}
五、赋值操作符
=、+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
=:赋值操作附,讲右边操作符的值附给右边的操作符。
+=:a += 2 ---> a = a + 2
-=:a -= 2 ---> a = a - 2
后面的几个操作符以此类推,相互等价的, 取模赋值操作符( % = ),后面的赋值操作符,涉及到的内容后续在一一介绍。
赋值操作符(=),之外操作符都属于复合赋值操作符,它结合了简单的数学运算。
复合赋值操作符:通常用于简化代码,是代码更加简洁、在使用复合赋值操作附时需要注意运算的优先级,以确保与预期结果相符。
int main()
{
int a = 5;
a += 3;
a -= 3;
a *= 3;
a /= 3;
a %= 3;
return 0;
}
六、条件操作符
? :
别看着慌,用法,和基本结构很容易理解:表达式1 ?表达式2 :表达式3。条件操作符,是三目操作符,表达式2,和表达式3分别在表达式1的返回值为真或假时执行。
在进行的逻辑判断时,也许我们可以使用条件操作符,而不使用if语句.
对以下代码进行改造:
int main()
{
int a = 0;
int b = 0;
scanf("%d", &a);
if (a > 5)
b = -3;
else
b = 3;
return 0;
}
使用条件表达式对其进行改造后:
int main()
{
int a = 0;
scanf("%d", &a);
int b = a > 5 ? -3 : 3;
return 0;
}
使用条件操作符对if语句进行改变后是代码变得更加简洁。
当表达式1,为真执行表达式2,反之执行表达式3。
七、逗号表达式
,
逗号表达式(,),在C语言里我们可以将多个表达式按照一定的顺序使用逗号表达式排放在一起,当执行逗号表达式时,从左至右依次执行,整个表达式返回的结果是最后一个表达式的结果
表达式1,表达式2,表达式3,...... ,表达式n
示例:
#include <stdio.h>
int main()
{
int a = 10;
int b = 5;
int c = 4;
if (b = a + c, ++c, a++, a > b)
{
printf("%d\n", a);
}
else
{
printf("%d\n", b);
}
return 0;
}
你们认为这串代码是怎么运行的,在if语句里的逗号表达式是怎么执行的是直接到最后一条表达式进行判断然后打印a吗?
最后输出的结果是14,因为逗号表达式是从左到右依次执行,虽然那些表达式的值没有被返回,但也是被执行过的。
一般在使用逗号表达式的场景是在for循环语句里,对多个变量的初始化,迭代里使用。
限制:1、表达式不能包含在其他表达式中,如赋值或比较表达式
2、减少对逗号表达式的使用,它降低了代码的可读性,使代码不够简洁。
八、逻辑操作符
&&、||、!
表达式1 && 表达式2
表达式1 || 表达式2
!false
逻辑操作符:
&&:逻辑与操作符,包含并且的意思,是双目操作符,用于判断左右两个表达式的真假,如果表达式1,或者表达式2为假,那整个句子的结果为假,表达式1、2均为假返回值为假,左右表达式全真结果为真。
||:逻辑或操作符,是双目操作符,两侧至少有一个操作数为真,则结果为真,左右两个操作数为假,结果才为假。
!:逻辑取反操作符,是单目操作符,对操作数进行逻辑取反,真的变假的。
8.1逻辑操作符的基础认知
逻辑与操作符:
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
if (a > 0 && b > 0)
{
printf("a = %d , b = %d \n");//a大于0并且b大于0
}
else
{
printf("a = %d , b = %d", a, b);//a小于0或者b小于0,或者a与b均小于0
}
return 0;
}
逻辑或操作符:
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
if (a > 0 || b <= 0)
{
printf("a = %d , b = %d \n");//a大于0或者b小于等于于0执行,或者a大于0,b也小于等于0执行
}
else
{
printf("a = %d , b = %d", a, b);//a不大于0,并且b不小于等于0
}
return 0;
}
逻辑取反操作符:
反转布尔值,如果操作数为真,则结果为假;如果操作数为假,则结果为真。
int main()
{
int flag = 6;
printf("设立一个真的flag,去完成它!:");
scanf("%d", &flag);
if (!flag)
{
printf("出了一点小意外,我的flag失效的wuwuwu~\n");
}
else
{
printf("我要学好编程!!!\n");
}
return 0;
}
8.2实现逻辑操作符的案例
请编写一段代码对输入的年份进行判断是否为闰年:
1、该年份能被4整除,但不能被100整除,则为闰年
2、该年份能被400整除,则为闰年
条件一:能被4整除说明 year % 4 == 0,但不能被100整除说明在能被4整除的基础上接着增加了一个限制条件,year % 100 != 0,写在一起:year % 4 == 0 && year % 100 != 0。
条件二:只需求year能被400整除:year % 400 == 0
结合起来代码可以这样写
int main()
{
int year = 0;
scanf("%d", &year);
if (year % 4 == 0 && year % 100 != 0)
printf("是闰年\n");
else if (year % 400 == 0)
printf("是闰年\n");
return 0;
}
实际上我们可以对上述代码进行优化,由上述代码我们可以知道,if 和 else if 我们只需要执行其中一条语句就可以完成判断,我们不妨使用逻辑或表达式,将它们放在一起。
int main()
{
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
printf("是闰年\n");
return 0;
}
8.3逻辑与逻辑或的短路行为
逻辑与和逻辑或在使用里存在着一些小问题。
逻辑或:当判断逻辑与的第一个操作数为真,便不会执行之后的操作数、操作符,因为整个表达式的结果已经被计算为真。
逻辑与:同理,当判断逻辑或的第一个操作数为假时,便不在会执行之后的操作数、操作符。
实例:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a++ && b--)
{
printf("%d %d\n", a, b);
}
return 0;
}
对于if判断,你想想最后会打印a 和 b 会打印多少?
实际上它什么也没有,因为我们知道,后置++是先执行在++,简单说就是先使用a在逻辑或里进行判断,a在加上1。a的值为0进行逻辑与判断后if语句并不会执行,而且b的值也不会减去1,还是等于2
这次就能更好的理解逻辑或的短路,下例代码执行后会a 和 b分别是多少?
int main()
{
int a = 0;
int b = 2;
if (++a && b--)
{
printf("%d %d\n", a, b);
}
return 0;
}
结果为 1 , 1你有没有算对呢?
有了对逻辑或的短路问题的理解后,对于理解逻辑于短路原理,也相差不大。
请对a, c, b, d 的值为多少进行判断。
int main()
{
int i = 0, a = 0, b = 1, c = 3;
i = a++||++b||c++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c);
return 0;
}
结果是多少呢?首先我们知道逻辑或在对第一个操作数进行判断后,条件为真不会在执行,这里a=0,条件为假,不为真所以会继续执行下一个操作数,这里前置++的用法为先对b加上1,然后在使用b进行判断,这里判断结果为真便不会执行之后的操作符、操作数。综上,a = 1,b = 2, c = 3
九、单目操作符
+、-、!、++、--、*、~、&、|、sizeof、类型转换
这里,*, ~, &, |暂且不先介绍,内容放在下篇。
9.1单目操作符基础理解
+(正号)数值取正,通常省略、-(负号)将数值取反
++:将变量的值加1
前置++,先对变量加1后在进行运算。
后置++,先对变量进行运算后在加上1。
--:将变量的值减1
前置--,先对变量减1后在进行运算。
后置--,先对变量进行运算后在减1去1。
前置:先加加(减减),再使用。后置: 先使用,再加加(减减)。
sizeof:获取类型或者变量的大小。
类型转换:将一个变量的类型强制转换为其它类型,但在使用中可能会丢失精度。
9.2代码示例
前置++(--),后置++(--)
int main()
{
int a = 3;
int b = 3;
printf("a = %d, b = %d\n", ++a, --b);
//printf("a = %d, b = %d", a--, b--);
return 0;
}
最终打印结果为多少?
对于前置,我们知道,是先对变量加1(减1),在使用它时先加上1(减1), 再进行运算,所以第一个printf打印的值为 a = 4, b = 2。
同理,对于后置,是先使用,再对变量加1(减1),所以第二个printf打印的值为 a = 3, b = 3。
sizeof:一般用来获取类型或者变量的大小,但我们还可以用来计算数组元素个数。
int main()
{
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(char));
int arr[] = { 4, 2, 67, 8, 2, 4, 0, 6, 9, 1, 34 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
return 0;
}
对于使用sizeof计算类型大小时,要注意,计算的结果大小单位为字节。
再使用sizeof时既然可以用来计算变量的大小,那我们可以先sizeof(arr)来计算数组的大小,由于数组是整形,有11个变量(不需要慢慢一个一个去数),所以结果为44字节,我们再取出数组内的一个变量计算结果为4,这样两者相除就可以计算数组的元素个数,以后可不用吭哧吭哧的去数了。
强制类型转换:我不建议使用这个操作,它会损失数据的精度
int main()
{
double a = 3.14;
int b = (int)a;
printf("%lf %d", a, b);
}