这是C语言初阶的第三篇文章,今天就让我们来详细解读一下,C语言中的操作符!!
一起来看看吧!
(友情提示:本篇篇幅也许过长,最好不要跳跃式浏览)
文章目录- 1. 什么是操作符
- 2. 操作符详解
- 2.1 算术操作符☀️
- `/ 除`
- `% 求余`
- 2.2 位移操作符☀️
- 使用演示
- `<< 左移操作符`
- `>> 右移操作符`
- 2.3 位操作符☀️
- `& 按位与(同 1 为 1 )`
- `| 按位或(有 1 为 1)`
- `^ 按位异或(相同为 0,相异为 1)`
- 2.4 赋值操作符☀️
- `=`
- `+=(此类是复合赋值操作符)`
- 2.5 单目操作符☀️
- `!逻辑反操作`
- `+ 正值 - 负值`
- `& 取地址`
- `sizeof 取操作数类型的长度(以字节为单位)`
- `~ 按位取反`
- `++ -- `
- `* 解除引用操作符`
- `(数据类型) 强制类型转换`
- 2.6 关系操作符☀️
- 2.7 逻辑操作符☀️
- `&&`
- `||`
- 2.8 条件操作符(三目操作符)☀️
- 2.9 逗号表达式☀️
- 2.10 下标引用操作符☀️
- `[]`
- 2.11 函数调用操作符☀️
- `()`
- 2.12 结构体成员访问操作符☀️
- `.`
- `->`
- 3. 结尾
1. 什么是操作符
在C语言中,操作符可以分为
算术操作符 ☀️
- + - * / %
位移操作符☀️
- >> <<
位操作符☀️
- &、|、^
赋值操作符☀️
- = += -=、*= /= &= ^= |= >>= <<=
单目操作符☀️
- ! - + & sizeof ~ -- ++ * (数据类型)
关系操作符 ☀️
- > >= < <= != ==
逻辑操作符 ☀️
- && ||
条件操作符(关系操作符) ☀️
- exp1 ? exp2 : exp3
逗号表达式 ☀️
- exp1 , exp2 , exp3, exp4 ……
下标引用操作符 ☀️
- []
函数调用操作符☀️
- ()
结构体成员访问操作符☀️
- . ->
没错,在C语言中从 加+ 减- 乘* 除/,到访问数组所需要的 [] 统统都属于操作符。
那么这些操作符究竟该如何使用呢?它们的使用有什么需要注意的地方吗?下面就让我来给大家一 一详细的介绍一下!
冲鸭!!!
2. 操作符详解
2.1 算术操作符☀️
算术操作符中的 + - * 就与我们平常计算时候一样,没有什么需要太需要注意的点。
/ 除除号两边操作数的类型不同,执行的操作也有一定的区别
如果两边操作数都为整数,执行整数除法
% 求余如果两边的操作数
有一个是浮点数,执行浮点数除法
求余符号的两边必须都是整数才能进行运算
这是正确的使用方法,但是如果操作数有浮点数出现,那么就会……
编译都无法通过!!!!!!!
2.2 位移操作符☀️
位移操作符
只能操作整型数据
而且操作整型在内存中存储的二进制数据!
根据我们在之前整型提升的博客中我们学习到,整型在内存中存储的形式是二进制的补码表现形式,所以位移操作符操作的就是
整型补码
使用演示 << 左移操作符
整型提升博客指路:【C语言初阶】 为什么我的两个整数加起来结果不对?原来是你,整型提升~
把
整数的二进制补码
向左位移,左边丢弃,末尾补 0
正数
2 << 1 00000000000000000000000000000010 (补码) === 2 00000000000000000000000000000100 (补码) === 4
>> 右移操作符
负数
-2 << 1 10000000000000000000000000000010 (原码) === -2 11111111111111111111111111111101 (反码) 11111111111111111111111111111110 (补码) 左移,左边丢弃,末尾补 0 11111111111111111111111111111100 (补码) 11111111111111111111111111111011 (反码) 10000000000000000000000000000100 (原码) === -4
右移操作符,有两种位移方式,相对来说有些复杂(并且,两种位移方法取决于编译器和用法没有关系)
-
算术右移
右边丢弃,在左边补原符号位
正数
2 >> 1 00000000000000000000000000000010 (补码) === 2 右移,右边丢弃,左边补 符号位 00000000000000000000000000000001 (补码) === 1 > 1" />
负数
-2 >> 1 10000000000000000000000000000010 (原码) === -2 11111111111111111111111111111101 (反码) 11111111111111111111111111111110 (补码) 右移,右边丢弃,左边补 符号位 11111111111111111111111111111111 (补码) 11111111111111111111111111111110 (反码) 10000000000000000000000000000001 (原码) === -1 > 1" />
-
逻辑右移
右边丢弃,在左边补 0
正数
2 >> 1 00000000000000000000000000000010 (补码) === 2 右移,右边丢弃,左边补 符号位 00000000000000000000000000000001 (补码) === 1 > 1 (逻辑右移)" />
负数
-2 >> 1 10000000000000000000000000000010 (原码) === -2 11111111111111111111111111111101 (反码) 11111111111111111111111111111110 (补码) 右移,右边丢弃,左边补 0 01111111111111111111111111111111 (补码) === (原码) === 2147483647 > 1 (逻辑右移)" />
(PS:操作符使用时,无论是 << 左移操作符 ,还是 >> 右移操作符
不会改变操作数的原值
即:int a = 2; int b = a >> 1; 两个语句执行之后 a 的值不变)
2.3 位操作符☀️
位操作符同位移操作符的操作数一样
只能操作整数,且操作二进制的补码
什么叫同 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("%dn", sizeof(a));
printf("%dn", 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("%dn", 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 = %dn", max);
上边的代码,可以判断 a 和 b 谁大
我们可以用三目操作符直接写成
int a = 100;
int b = 40;
int max = 0;
max = a > b ? a: b;
printf("max = %dn", max);
两端代码最终的结果都是
所以对于条件操作符 exp1 ? exp2 : exp3exp1 为真,则整个表达式的结果为 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 = %dna = %dnc = %.2lfnmax = %dn", 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 %dn", 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 %dn", z.name, z.age, z.hight, z.weight);
struct Student* pz = &z;
printf("%s %d %d %dn", pz->name, pz->age, pz->hight, pz->weight);
操作符" />
可以看出,使用 结构体指针 -> 结构体成员,也可以访问结构体成员
以上就是C语言中所有的操作符啦!
在任何语言中,操作符都是非常重要的一部分。如果不能对操作符有着熟练、深刻的理解,那么就不可能熟悉任何一门语言!
如果不能熟练掌握、深刻理解操作符的使用,那么对于数据的处理,肯定是不行的。
3. 结尾
本篇关于 C语言中操作符的详细解释就已经结束啦!!
但是关于操作符的使用,在我们写代码的时候还会有一些陷阱出现,明天我会把类似的一些陷阱 或者 使用的错误,以及 操作符的优先性、结合性统一讲解一下!
OK!本篇博客到此就结束啦!!!
如果有写的不好的地方,欢迎大家提出宝贵的建议或者意见。
撒花!!
最后 的 最后
求点赞 关注 评论 收藏 ⭐️ 啊!!
求点赞 关注 评论 收藏 ⭐️ 啊!!
求点赞 关注 评论 收藏 ⭐️ 啊!!


![【C语言初阶】[万字] 你见过这样用的操作符吗?这里可能有你不知道的操作符知识~ [ C语言操作符详解 一 ] 【C语言初阶】[万字] 你见过这样用的操作符吗?这里可能有你不知道的操作符知识~ [ C语言操作符详解 一 ]](http://www.mshxw.com/aiimages/31/648275.png)
