C语言操作符的那些事(详细全)
前言 C语言中操作符不多,但是有些相同的操作符都是在不同的表达式中,有不同的解释意思,比如 * 号,在表达式中5*5表示乘号,在int *p表示指针,在 *p = 10中,又表示解引用,所以今天就来详细的整理一下C语言中的操作符,做到心中有数,可以一眼识破,用途有哪些。重点不是记忆:是理解,兄弟们,要懂本质。
文章目录
- 前言
- 操作符的分类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用符
- 函数调用符
- 结构体调用操作符
操作符的分类 注意:以下操作符都必须是英文的半角符号。
算术操作符 | + * / % |
---|---|
移位操作符 | << 左移操作符 >> 右移操作符 |
位操作符 | &按位与 ^ 按位异或 |按位或 |
赋值操作符 | = += -= *= /= &= |= ^= |
单目操作符 | ! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一数的二进制按位取反 -- 前置 、后置- - ++前置 后置++ |
关系操作符 | > < <= >= == != |
逻辑操作符 | &&逻辑与 ||逻辑或 |
条件操作符 | exp1 ? exp2 : exp3 |
逗号表达式 | exp1, exp2, exp3, …expN |
下标引用 | [ ] 下标引用操作符 |
函数调用 | ( ) |
结构成员 | . 结构体.成员名 -> 结构体指针->成员名 |
+
加;-
减;*
乘;/
除;%
取余(取模);
+
-
*
没什么好说的,大家都会用。来看看这个
/
号;
?问:下面代码的 a = ?
int a = 9 /2;
printf("%d",a);
答:结果为4;不是4.5;在C语言中:对于 / (?除号来说),执行的是整数除法。对于:/ (?除号),执行的是整数除法,这句话的深度理解;
?问:下面代码的 b = ?
double b = 9 /2;
printf("%d",b);
答:结果为:4;这说明,对于 / 来说,执行的是整数除法,类型与它无关;?问:假如要得到小数的除法如何得到呢?
答:只要操作符 / (?除号) 两边的操作数,只要有一个操作数为浮点数(小数),即执行的为浮点数除法,即得到的值为小数;
double a = 9 / 2.0;
double b = 9.0 /2.0;
double c = 9.0 / 2.0;
a b c的结果都是小数:4.5
来看看取模操作符
%
:取模也就是取余,一个数对另一个数取余就是得到这个数的余数:
int a = 9 % 4;
printf("%d",a);
a 的结果为 1;就是 9除4 余数为1的结果:
有个记忆小技巧:取模可以想象成为一直磨掉一个数磨到不能再磨为止;比如:9 % 2,可以想象为 9 磨掉 2,在磨掉 2,再 磨掉 2,再 磨掉 2,剩下 1,磨不了了,结果就为1了。
注意:取模操作符
%
,两边的操作数必须为整数;下面,不为整数会报错;文章图片
移位操作符
计算方式:>>
左移操作符 和<<
右移操作符;
- 左移操作符就是左边丢弃,右边补0;
- 右移操作符分两种:
- 逻辑右移:右边丢弃,左边补0;不管是不是正数还是负数(作为了解即可)
- 算术右移:右边丢弃,左边看是负数还是正数,负数补上1,正数数补上0;(几乎所有编译器都是按照这种方式计算)
- 更准确的说,算术右移:右边丢弃,左边看最高位是1 还是 0 ,是 1 则补上 1,是 0 则补上 0;
移位操作符,操作的都是二进制的数。
并且操作数都是整数,且右操作数不能为负数(了解即可)
看看左移操作符
<<
:?问:下面代码的 b = ?
int a = 5;
int b = a<<1;
printf("%d",b);
结果为:10;
分析分析:a 是整数,在内存中以补码的形式存储,正数的补码和源码时一致的,a 是 int 类型,占四个字节;如下图:
文章图片
?问:下面代码的 b = ?
int a = -1;
int b = a<< 1;
printf("%d",b);
结果为:-2;
分析图如下:
文章图片
看看右移操作符
>>
:右移操作符,我们见到的都是属于算术右移,因为,对于计算机的整数来说,分为,正数和负数,正数和负数的区分是通过高位的第一个位来区分的,0,表示正数,1表示负数;所以在执行有一操作符时候?问:下面代码的 b = ?>>
,需要判断右边补上的是0还是1,这是根据你要操作的是负数还是正数决定的。
这是正数情况:
int a = 5;
int b = a>> 1;
printf("%d",b);
结果:b= 2
文章图片
?问:下面代码的 b = ?
int a = -1;
int b= a>>1;
printf("%d",b);
结果为:b = -1;
分析图如下:
文章图片
总结:
m左移n位的结果:就是m × 2n;
m右移n位的结果:就是 m / 2n;
位操作符
注意:他们的操作数必须是整数的二进制数。&
按位与
|
按位或
^
按位异或
计算方式:
&
按位与 ,位数都为1,结果才为1,其他情况为0;|
按位或 ,位数只要有一个为1,结果就为1,全为0结果才为0;^
按位异或,位数不同就为1,相同就为0;看看
&
按位与:?问:下面代码的 c = ?
int a = 5;
int b = -2;
int c = a&b;
printf("%d",c);
结果为:c = 4
分析图:
文章图片
看看
|
按位或:?问:下面代码的 c = ?
int a = 4;
int b = -2;
int c = a|b;
printf("%d",c);
结果为:c = -2
分析图如下:
文章图片
看看
^
按位异或:?问:下面代码的 c = ?
int a = 5;
int b = -2;
int c = a^b;
printf("%d",c);
结果:c = -5
分析图如下:
文章图片
?问:位操作符有什么作用呢?
答: 可以计算一个数的二进制补码中有多少个1如下代码:
假如有个变量a ,而 a&1 就可以得到a的最后一位是0还是1;
如果要得到a 的二进制一共有多少个1,可以让 a右移后,a>>1,得到的结果继续与 1 按位与 a&1,用一个变量加循环就可以统计 a 中有多少个1;
int a = 15;
int count = 0;
int ret = 0;
int i = sizeof(int)*8;
while(i > 0)
{ ret = a & 1;
if(ret == 1)
count++;
a = a>>1;
i--;
}
printf("count = %d",count);
异或操作符还可以用于:不用创建第三个变量交换两个整数
int a = 5;
int b = 6;
a = a^b;
b = a^b;
c = c^b;
printf("a = %d,b = %d ",a, b);
结果位: a = 6;
b=5;
赋值操作符 赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
其实这个操作符很简单,基本没什么可以讲的,所以就跳过了;+=
-=
*=
/= ``%=
>>=
<<= ``&=
|=
^=
单目操作符
来看看! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置
、后置--
++ 前置
、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
! 逻辑反操作
:就是把一个数按逻辑变为 0 或者 1;得到的结果为bool值.
int a =5;
a = !a;
printf("a = %d",a);
int b = 0;
b = !b;
printf("b = %d",b);
结果:a = 0;
b=1;
来看看 :
& 取地址
,* 间接访问操作符(解引用操作符)
:& 取地址
,是取出变量的地址,如果对数组名取地址,则取出的是整个数组的地址;* 间接访问操作符(解引用操作符)
,在定义时候 * 表示变量是指针,在使用的时候, * 表示指针指向的变量。int a = 10;
&a 得到的是 a 的地址int * p = &a;
这里的* 表示 p是一个指针变量;*p = 20;
这里的*表示指针p所指向的变量a
//
//
int arr[10]={
0};
sizeof(&arr)本质,&arr是地址,地址的类型为指针类型,所以大小为4个字节sizeof(arr)得到的是 40个字节,在sizeof(),里面放入数组名,表示求整个数组的大小,并不代表数组名是首元素的地址,在这里是一个例外。
来看看 :
sizeof 操作数的类型长度(以字节为单位)
:- 首先必须认识,sizeof 不是函数,函数只能用函数调用符号(),来调用,而 sizeof,是不需要调用符号也可以使用的,
- sizeof(),计算的是括号里面的变量或者类型的字节大小,与存放的数据无关;
- sizeof(),括号内的表达式,不参与计算,括号内的表达式在编译阶段就已经定好了。
int a = 10;
char arr[10] = "abcdef";
printf("%d",sizeof(a));
//结果:4
printf("%d",sizeof(int));
//结果:4
printf("%d",sizeof a);
//结果:4
printf("%d",sizeof(arr));
//结果:10
printf("%d",sizeof(arr[0]));
//结果:1
//
//
int a = 5;
short s = 10;
printf("%d",sizeof(s = a +2));
// 这里的值为2,因为s的类型为short,
//s的值为7,但是在编译阶段就确定了,运行时候,不会改变下面的 s的值
printf("%d",s);
//这里s结果为:10;
来看看 :
~ 对一个数的二进制按位取反
:就是对一个整数的二进制数补码进行取反操作
int a = 0;
int b = ~a;
printf("b = %d",b);
结果:b = -1;
分析图:
文章图片
来看看 :
-- 前置
、后置--
++ 前置
、后置++
- - 前置减减
,就是先减1后使用;++前置加加
,就是先加1后使用;后置- -
,就是先使用,后减1;后置++
,就是先使用,后加1;
//++和--运算符
//前置++和--
#include
int main()
{int a = 10;
int x = ++a;
//先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。int y = --a;
//先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
return 0;
}//后置++和--
#include
int main()
{int a = 10;
int x = a++;
//先对a先使用,再增加,这样x的值是10;之后a变成11;int y = a--;
//先对a先使用,再自减,这样y的值是11;之后a变成10;
return 0;
}
关系操作符
其实就是比较大小,返回的值为bool值,0或者 1;>
<
<=
>=
==
!=
注意:在编程的过程中== 和=不小心写错,导致的错误。
逻辑操作符
&& 逻辑与,||逻辑或
逻辑与 &&
就是 左右两边的操作数同时为真,结果为真;注意要点:当最前面的操作数为假,后面的操作数就不执行了;
逻辑或 ||
就是左右两边的操作数只要有一个为真,就为真;注意要点:当最前面的操作数为真,后面的操作数就不执行了;
来看一道360面试题
?问:程序输出的结果是什么?
#include
int main()
{int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
结果为: a = 1 b =2,c =3 d =4;
分析:
文章图片
记住逻辑&& 第一个操作数为0,后面的表达式都不用计算了。
?问:程序输出的结果是什么?
#include
int main()
{int i = 0,a=0,b=2,c =3,d=4;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
结果: a = 1 b =3,c=3 d =4;
分析:
文章图片
逻辑或 | | :当最前面的操作数为真,后面的操作数就不执行了;
条件操作符
exp1 ? exp2 : exp3条件操作符通草用来书写一些简单的if else 语句;
exp表示的是表达式
如:
if (a > 5)
b = 3;
else
b = -3;
转换成条件表达式,是什么样?
int b = a > 5 ? 3:-3;
或者:
a > 5 ? b = 3: b = -3;
//不常用
逗号表达式
exp1, exp2, exp3, …expN逗号表达式最终的结果为expN,即逗号最后一个表达式的值;
通常我们在使用的时候,加上括号(),会使得代码可读性好,如(exp1, exp2, exp3, …expN);
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
//逗号表达式
c是多少?
分析: a>b结果为 0,a =b+10,结果为12,b=a+1结果为 13;所以表达式化简为:(0,12,13);
所以结果为 13
结果:c = 13;
下标引用符
[ ]下标引用符就是一个中括号的模样,用于访问数组下标对应的值。
操作的数据:一个数组名+一个索引下标
int arr[10];
//这是用于定义数组
arr[9] = 10;
//这是使用下标引用符访问数组9[arr] 得到的是下标索引为9对应的值,深刻理解操作符,9和arr就是[ ]操作符的操作数;
我们要了解数组名就是首元素的地址;
arr = &arr[0];函数调用符
所以:arr+1 = &arr[1];
所以*(arr+1)= arr[1];
()函数调用符号就是一个小括号
操作数为:函数名+参数列表(参数列表可以为空)
int test(int x);
//这里是声明函数的意思
int test(int x)//这里是定义函数的意思
{}
test(10);
//这里是调用函数的意思
结构体调用操作符
. 和 ->操作数:自定义的变量 + 自定义类型里面的成员变量,计算的结果返回的是右边操作数的类型。
在C语言中,允许用户自定义数据类型,通过struct关键字去定义:
如
struct student
{ char name[20];
int age;
};
这里自定义了一个类型为 struct student; 里面包含了 name 数组和 age 变量;假如你要访问里面的变量的化,你可以通过自定义的类型 定义一个变量,通过结构体访问符去访问。
比如我要访问 name数组:
struct student s;
//定义的类型 定义一个变量s
s.name ;
//你访问的就是name数组
【C语言操作符的那些事(详细全)】假如你是自定义类型的指针
struct student s;
s->name;
//你访问的就是name数组
推荐阅读
- Shell-Bash变量与运算符
- 2.6|2.6 Photoshop操作步骤的撤消和重做 [Ps教程]
- MongoDB,Wondows下免安装版|MongoDB,Wondows下免安装版 (简化版操作)
- 【生信技能树】R语言练习题|【生信技能树】R语言练习题 - 中级
- 一起来学习C语言的字符串转换函数
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- C语言浮点函数中的modf和fmod详解
- C语言中的时间函数clock()和time()你都了解吗
- 在线版的迅捷思维导图怎么操作()
- 操作系统|[译]从内部了解现代浏览器(1)