这是C语言初阶的第三篇文章,今天就让我们来详细解读一下,C语言中的操作符!!
一起来看看吧!
(友情提示:本篇篇幅也许过长,最好不要跳跃式浏览)
文章目录
1. 什么是操作符💦
🌀在C语言中,操作符可以分为 🌀
📌
算术操作符 ☀️
+
-
*
/
%
位移操作符☀️
>>
<<
位操作符☀️
&
、|
、^
赋值操作符☀️
=
+=
-=
、*=
/=
&=
^=
|=
>>=
<<=
单目操作符☀️
!
-
+
&
sizeof
~
--
++
*
(数据类型)
关系操作符 ☀️
>
>=
<
<=
!=
==
逻辑操作符 ☀️
&&
||
条件操作符(关系操作符) ☀️
exp1 ? exp2 : exp3
逗号表达式 ☀️
exp1 , exp2 , exp3, exp4 ……
下标引用操作符 ☀️
[]
函数调用操作符☀️
()
结构体成员访问操作符☀️
.
->
没错,在C语言中从 加+
减-
乘*
除/
,到访问数组所需要的 []
统统都属于操作符。
🌊那么这些操作符究竟该如何使用呢?它们的使用有什么需要注意的地方吗?下面就让我来给大家一 一详细的介绍一下!
冲鸭!!!🌊🌊🌊
2. 操作符详解💧
2.1 算术操作符☀️
算术操作符中的 +
-
*
就与我们平常计算时候一样,没有什么需要太需要注意的点🐥。
/ 除
除号两边操作数的类型不同,执行的操作也有一定的区别👇
📌
如果两边操作数都为整数,执行整数除法
🌸
如果两边的操作数
有一个是浮点数,执行浮点数除法
🌸
% 求余
📌
求余符号的两边必须都是整数才能进行运算
🌸
这是正确的使用方法,但是如果操作数有浮点数出现,那么就会…… 👇
编译都无法通过!!!!!!!
2.2 位移操作符☀️
位移操作符
只能操作整型数据
而且操作整型在内存中存储的二进制数据!
根据我们在之前整型提升的博客中我们学习到,整型在内存中存储的形式是二进制的补码表现形式,所以位移操作符操作的就是👇
整型补码
📌
整型提升博客指路:
使用演示
<< 左移操作符
把
整数的二进制补码
向左位移,左边丢弃,末尾补 0
📌
正数
2 << 1
00000000000000000000000000000010 (补码) === 2
00000000000000000000000000000100 (补码) === 4
📌
负数
-2 << 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
左移,左边丢弃,末尾补 0 11111111111111111111111111111100 (补码)
11111111111111111111111111111011 (反码)
10000000000000000000000000000100 (原码) === -4
>> 右移操作符
右移操作符,有两种位移方式,相对来说有些复杂(并且,两种位移方法取决于编译器和用法没有关系)
-
算术右移
右边丢弃,在左边补原符号位
📌
正数
2 >> 1
00000000000000000000000000000010 (补码) === 2
右移,右边丢弃,左边补 符号位 00000000000000000000000000000001 (补码) === 1
📌
负数
-2 >> 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
右移,右边丢弃,左边补 符号位 11111111111111111111111111111111 (补码)
11111111111111111111111111111110 (反码)
10000000000000000000000000000001 (原码) === -1
-
逻辑右移
右边丢弃,在左边补 0
📌
正数
2 >> 1
00000000000000000000000000000010 (补码) === 2
右移,右边丢弃,左边补 符号位 00000000000000000000000000000001 (补码) === 1
📌
负数
-2 >> 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
右移,右边丢弃,左边补 0 01111111111111111111111111111111 (补码) === (原码) === 2147483647
(PS:操作符使用时,无论是 << 左移操作符
,还是 >> 右移操作符
不会改变操作数的原值
即:int a = 2; int b = a >> 1;
两个语句执行之后 a
的值不变)
2.3 位操作符☀️
位操作符同位移操作符的操作数一样
只能操作整数,且操作二进制的补码
& 按位与(同 1 为 1 )
什么叫同 1 为 1?直接上例子👇
📌
3 & 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
同 1 为 1 00000000000000000000000000000001 (补码) === 1
📌
-2 & 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
同 1 为 1 00000000000000000000000000000010 (补码) === 2
从上边的例子我们可以看出 同 1 为 1
,就是 两数二进制位的相同位置都为 1
时,结果的二进制位对应位置为 1
包括符号位
| 按位或(有 1 为 1)
按照相同的规律👇
📌
3 | 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
有1 为 1 00000000000000000000000000000111 (补码) === 7
📌
-2 | 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
有 1 为 1 11111111111111111111111111111110 (补码) === -2
^ 按位异或(相同为 0,相异为 1)
📌
3 ^ 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
相同为 0,相异为 1 00000000000000000000000000000110 (补码) === 6
📌
-2 ^ 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
相同为 0,相异为 1 11111111111111111111111111111100 (补码) === -4
希望大家不要眼花缭乱啦!!!
2.4 赋值操作符☀️
=
将右值(右变量的值)赋予左变量👇
int a = 10;
int b = a;
则
a === 10;
b === 10;
赋值操作符可连续赋值👇
int a = 5;
int b = 0;
int c = 10;
a = b = c + 1;
则
a === 11;
b === 11;
c === 10;
//但是这样的写法,可读性差,也不利于调试,所以最好不要这样写
b = c + 1;
a = b;
//这种写法可以实现同样的效果,但是可读性好,且利于调试
+=(此类是复合赋值操作符)
a += 1; ==> a = a + 1;
其他的赋值操作符只需要注意除等号外操作符的特点就好了!!
2.5 单目操作符☀️
目,就是指操作数的个数
单目,就是只有一个操作数
!逻辑反操作
在C语言中
0
为假, != 0
为真
我们就可以用下面的例子来解释 !
:
我们看到
第一个判断条件,
如果为真,输出 !!!
如果为假,输出 ~~~
第二个判断条件
如果为真,输出000
如果为假,输出666
两个判断,输出的结果是
!!!
和 666
判断出 !0
为 真,!1
为 假
所以 !
的作用就是👇
将假 逻辑反为真,将真 逻辑反为假
+ 正值 - 负值
-
:
将正数变为负数,负数变为正数
& 取地址
&
:可以取出变量的地址
int a = 10;
&a; //这样就取出了 变量 a 的地址
但是 不可以对常量取地址
🌸
&1;
&2;
&3;
//这些都是不可以的
sizeof 取操作数类型的长度(以字节为单位)
sizeof
:sizeof(数据类型);
此时sizeof
的值就是 所操作数据类型的长度
int a = 10;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int b = sizeof(a);
int c = sizeof(arr);
则
b === 4;
c === 40;
//计算此数组类型的长度时,数组类型为 int [10] 即 长度为 10 的整型数组
虽然 sizeof
的使用,我们一般会加上 ()
但是sizeof
并不是一个函数
int a = 0;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
对于上面的代码,运行之后是这样的👇
所以
sizeof(a) <==> sizeof a
sizeof
只是一个操作符,并不是函数
~ 按位取反
只能操作整数
且操作二进制的补码
📌
~(-2)
11111111111111111111111111111110 (补码) === -2
按 位 取 反 00000000000000000000000000000001 (补码) === 1
++ --
++
:
++ <==> += 1
即 ++
是 自增 1
所以 --
就是 自减 1
但是前置、后置还有区别👇
📌
前置++(--)
++a;(--a;) //表示 先自增(自减),后使用
前置
++
,先自增,后使用
即a
先 自增1
,后赋值于b
所以a === 1;
b === 1;
📌
后置++(--)
a++;(a++;) //表示 先使用,后自增(自减)
后置
++
,先使用,后自增
即a
先 赋值于b
,后 自增1
所以a === 1;
b === 0;
🌸,只举了 ++
的例子,但是 --
是相同的,希望大家可以理解 ++
--
的用法
* 解除引用操作符
这不是 算术操作符中的 乘*
虽然字符相同,但是用在不同的地方,是不同的操作符
*
:
一般用于
对指针解引用
📌
int a = 100; int *pa = &a; *pa = 20; printf("%d\n", a);
上述代码编译运行可见,我们通过*(解引用操作符)
对指向a
的指针pa
解引用 来改变了a
的值。
也就是说pa
指向了a
的地址,我们通过对pa
解引用就找到了a
的值
这就是*(解引用操作符)
的作用,因为用*
可以通过pa
间接来找到a
,所以*
也被称作间接访问操作符
(数据类型) 强制类型转换
(数据类型)
:
顾名思义,就是可以强制转换数据的类型
用法👇
📌
大家先来看一组代码:double a = 0; a = 12 / 5; printf("%lf", a);
上述代码,执行之后发现,在编译的过程中编译器提出了警告,而且
a
虽然是double
类型,但是因为参与运算的两个数是 整型,
所以实际存入
a
的值还是整型,这样就造成了精度的缺失这时候我们可以用 (double) 来强制类型转换
double a = 0; a = 12 / (double)5; printf("%lf", a);
这个时候就解决了精度缺失的问题
因为(double)
强制将 整型的5
转换成了double
型的5.0
所以运算结果就成了double
型
对于 强制类型转换 还需要注意一点👇
📌
对于一下这样的代码,大家想一想有没有什么问题👇int a = 0; (int*) pa = &a;
这样的代码,在定义变量的时候,将类型用 () 将 int* 指针类型括了起来,好像更能说明 * 是和 int 结合的 表示 int* 类型的指针
还有这样的代码👇
int a = int(3.14);
在强制类型转换的时候,将需要转换的 数值 用
()
括了起来这两种代码都是有问题的!
对于第一种:
如果在定义的时候 将 类型 用
()
括起来了,那么 就会表示强制类型转换
,就起不到定义变量类型的作用了对于第二种:
()强制类型转换操作符
只能对类型使用,不能对数值使用
2.6 关系操作符☀️
关系操作符需要注意的点没有太多,主要用于作为判断条件时的使用👇
if(a < b);
while(a < b);
一般用于以上的 if
或者 while
语句中
表达式成立,则为真 === 1
不成立,则为假 === 0
(注意:在使用 ==
判断相等时,千万不要写成 =(赋值操作符)
)
2.7 逻辑操作符☀️
逻辑表达式的 &&(逻辑与)
||(逻辑或)
只关注表达式的真假!
和位运算符中的 &(按位与)
|(按位或)
,操作二进制位!
千万不要
搞混了!!!!
&&
逻辑与 怎么用呢?👇
📌
1.表示并且int age = 0; scanf("%d", &age); if(age > 0 && age < 18) {//我们可以用 &&(逻辑与) 来控制判断条件的范围,表示并且 // age > 0 并且 age <18 // 但是千万不能写成 0 < age < 18 printf("minors"); }
只要表达式有一个为假,则判断条件就为假
上边代码,我们编译运行之后,是非常正确的
但如果我们把条件写成0 < age < 18
这样的形式呢?
我们会发现尽管输入了不在范围内的数值,他还是输出了minors
这是因为<
是逻辑操作符只关注表达式的真假
上述判断条件,在判断的时候,
0
会先与age
比较
如果为真0 < age === 1
如果为假0 < age === 0
但 无论是 0 还是1
,都比 18 要小,所以0 < age < 18
一定为真
这就造成了错误的结果,所以需要控制判断变量在一个范围内时
一定不要连用 逻辑操作符
📌
2.左表达式不成立,右表达式不计算大家看一下以下代码会输出什么呢?
//代码 1 int a = 0; int b = 1; int c = 2; int i = 0; i = a++ && ++b && ++c; printf("%d %d %d", a, b, c);
这个代码又会输出什么呢?
//代码 2 int a = 0; int b = 1; int c = 2; int i = 0; i = ++a && ++b && ++c; printf("%d %d %d", a, b, c);
代码 1:输出 1 1 2
代码 2:输出 1 2 3
那为什么会出现这种情况呢?
是因为&&(逻辑与)
有一个规定,就是左表达式不成立,右表达式不计算
首先
a === 0;
a++
表示先使用a
再自增,所以a++ && ++b && ++c
中,最左端表达式 为假,右边的两个表达式不计算。就出现了a === 1,b === 1,c ===2
的情况
++a
表示先自增 再使用a
,所以++a && ++b && ++c
中,所有表达式都为真,所有表达式都进行计算。就出现了a === 1, b === 2, c === 3
的情况
如果理解了第二个规定,表示并且
也可以用这个规定解释!
||
📌
1.表示或者
和&&逻辑与
相同,可以用来控制 判断条件的范围👇int x = 0; scanf("%d", &x); if (x < 20 || x > 40) { printf("didi"); }
这表示,
x < 20
或x > 40
时,打印didi
只要表达式有一个为真,则判断条件就为真
📌
2.左表达式成立,右表达式不计算
逻辑或的判断逻辑,正好与逻辑与相反//代码 1 int a = 0; int b = 1; int c = 2; int i = 0; i = a++ || ++b || ++c; printf("%d %d %d", a, b, c);
//代码 2 int a = 0; int b = 1; int c = 2; int i = 0; i = ++a || ++b || ++c; printf("%d %d %d", a, b, c);
代码 1:输出 1 2 2
代码2:输出 1 1 2
这就说明了,||(逻辑或)
的规定左表达式成立,右表达式不计算
首先
a === 0
a++
表示先使用a
再自增,所以a++ && ++b && ++c
中,最左端表达式 为假,第二个表达式为真,第三个表示不计算。就出现了a === 1,b === 2,c ===2
的情况
++a
表示先自增 再使用a
,所以++a && ++b && ++c
中,最左端表达式 为真,后两个二个表达式不计算。就出现了a === 1,b === 1,c ===2
的情况
2.8 条件操作符(三目操作符)☀️
使用三目操作符可以省去一些冗余的代码
int a = 100;
int b = 40;
int max = 0;
if (a > b)
max = a;
else
max = b;
printf("max = %d\n", max);
上边的代码,可以判断 a
和 b
谁大
我们可以用三目操作符直接写成👇
int a = 100;
int b = 40;
int max = 0;
max = a > b ? a: b;
printf("max = %d\n", max);
两端代码最终的结果都是👇
📌
所以对于条件操作符 exp1 ? exp2 : exp3
exp1
为真,则整个表达式的结果为exp2
exp1
为假,则整个表达式的结果为exp3
2.9 逗号表达式☀️
先来看一组代码👇
int max = 0;
int i = 2;
int a = 7;
double c = 21.3;
max = ( i++, a--, c *= 10, 55 / 11);
printf("i = %d\na = %d\nc = %.2lf\nmax = %d\n", i, a, c, max);
上述代码,所有变量都经过计算改变了值,但输出 max
的值为 55 / 11 === 5;
所以
逗号表达式👇
从左向右依次计算,整个表达式的结果是最后一个表达式的结果
2.10 下标引用操作符☀️
[]
一般用于数组下标的访问
int arr[5] = { 1,2,3,4,5 };
arr[4]
中 ,[]
是操作符,arr
和 4
是操作数
📌
我们都知道,除了某些特定情况,数组名就是数组首元素的地址
所以arr[4] <==> arr + 4
然而
arr + 4 <==> 4 + arr
那么可不可以将arr[4]
写成[4]arr
呢?
实操一下!👇
从结果来看,我们的推断是可以的
那么这说明什么呢?这说明
[]
就只是一个操作符,只要符合语法,操作数的顺序是可以颠倒的
2.11 函数调用操作符☀️
()
函数调用操作符,顾名思义就是再调用函数时侯使用的操作符
对于
Test(); //操作数:Test
Add(2,3); //操作数:Add,2,3
2.12 结构体成员访问操作符☀️
.
使用方法
结构体变量名 . 结构体成员名
🌸
struct Student
{
char name[100];
int age;
int hight;
int weight;
};
struct Student z = { "zhangsan",18,175,130 };
printf("%s %d %d %d\n", z.name, z.age, z.hight, z.weight);
我们可以分别输出 姓名
年龄
身高
体重
说明我们用 .
访问量结构体内的成员
->
使用方法
结构体指针 -> 结构体成员
🌸
struct Student
{
char name[100];
int age;
int hight;
int weight;
};
struct Student z = { "zhangsan",18,175,130 };
printf("%s %d %d %d\n", z.name, z.age, z.hight, z.weight);
struct Student* pz = &z;
printf("%s %d %d %d\n", pz->name, pz->age, pz->hight, pz->weight);
可以看出,使用 结构体指针 -> 结构体成员
,也可以访问结构体成员
以上就是C语言中所有的操作符啦!
在任何语言中,操作符都是非常重要的一部分。如果不能对操作符有着熟练、深刻的理解,那么就不可能熟悉任何一门语言!
如果不能熟练掌握、深刻理解操作符的使用,那么对于数据的处理,肯定是不行的。
3. 结尾🌺
本篇关于 C语言中操作符的详细解释就已经结束啦!!
但是关于操作符的使用,在我们写代码的时候还会有一些陷阱出现,明天我会把类似的一些陷阱 或者 使用的错误,以及 操作符的优先性、结合性统一讲解一下!
OK👌!本篇博客到此就结束啦!!!
如果有写的不好的地方,欢迎大家提出宝贵的建议或者意见。
撒花!!🌺🌺
最后 的 最后 🌹
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!