操作符详解
Posted 正义的伙伴啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作符详解相关的知识,希望对你有一定的参考价值。
操作符的类别
1、算数操作符
+ - * / %
注意事项:
- "/"除法运算想要得到的是小数,除数与被除数都必须是小数 例如
float a=4/5; //此时a=0.000000
- "%"两边必须是整数
2、移位操作符
<<左移操作符 >>右移操作符
1.左移操作符
左边抛弃、右边补0
2.右移操作符
两种规则:
- 算数右移: 右边丢弃,左边补原符号位
- 逻辑右移:右边丢弃,左边补0
标准说明所有无符号整数执行的都是逻辑右移,但对于有符号值到底是采用逻辑移位还是算数右移取决于编译器
我用的编译器(vs 2019)采用的是算数右移
再此讲一下整数的二进制表示形式:
如果整数是四个字节,也就是32位
例如 a=-1
原码:10000000000000000000000000000001
反码:1111111111111111111111111111111111110
补码:1111111111111111111111111111111111111
3.位操作符
& 按位与
| 按位或
^ 按位异或(相同为0,相异为1)
位操作符的优先级很低,在运算中尽量用括号括起来,例如a&1= =0 是优先运算1==0;
例如:a=3 b=5
c=a&b
d=a|b
e=a^b
- a:00000000000000000000000000000011
- b:00000000000000000000000000000101
- c:00000000000000000000000000000001
- d:00000000000000000000000000000111
- e:00000000000000000000000000000110
操作符的用法
1.判断奇偶
根据,末尾是0还是1来决定,为0的是偶数,为1的是奇数
if((a&1)==0)//为偶数就进入条件
2.交换两数(不使用中间变量)
#include<stdio.h>
int main()
{
int a = 5, b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("%d %d", a, b);
return 0;
}
这里我们使用的是按位异或,按位异或有两个公式:
a^a=0
a^0=a
//所以第二部 b=a^b=a^(b^b)=a^0=a;
//第三步 a=a^b=a^b^a=b;
3.变换符号
正数:取反->+1->负数
负数:取反->+1->负数
#include<stdio.h>
int main()
{
int a = 5;
a = ~a + 1;
printf("%d", a);
return 0;
}
4.求一个数的二进制位有多少个1
这里用三种方法解决:
方法一:
int main()
{
int count=0;
int n;
scanf("%d", &n);
while (n)
{
if (n % 2 == 1)
count++;
n = n / 2;
}
printf("%d", count);
return 0;
}
不使用位操作符,n/2相当于n>>1,n%2相当于n&1,但是这个代码有一个缺陷,就是当n为负数的时候,答案就不正确了。改进代码如下:
#include<stdio.h>
int main()
{
int count=0;
unsigned int n;
scanf("%d", &n);
while (n)
{
if (n % 2 == 1)
count++;
n = n / 2;
}
printf("%d", count);
return 0;
}
方法二:
用位操作符进行改进:
#include<stdio.h>
int main()
{
int count=0;
int n;
scanf("%d", &n);
for (int i = 0; i < 32; i++)
{
if (((n >> i) & 1) == 1)
count++;
}
printf("%d", count);
return 0;
}
位操作符还有一种改进方法:使用表达式 n=n&(n-1)这个表达式每使用一次,n最右边的1就会消失,循环表达式下去直至n==0
#include<stdio.h>
int main()
{
int n;
scanf("%d", &n);
int count = 0;
while (n)
{
count++;
n = n & (n - 1);
}
printf("%d", count);
return 0;
}
4赋值操作符
=是一个最常用的赋值操作符,可以给变量赋值
赋值操作符支持连续赋值:x=y=1;
复合操作符
- +=
- -=
- *=
- /=
- %=
- <<= 和>>=
- &=
- |=
- ^=
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置/后置--
++ 前置/后置++
* 解引用操作符
(类型) 强制类型转换
注意:sizeof的返回值是一个unsigned int类型的数
sizeof和数组
#include<stdio.h>
void fun1(int arr[])
{
printf("%d\\n", sizeof(arr));
}
void fun2(char ch[])
{
printf("%d\\n", sizeof(ch));
}
int main()
{
int arr[10] = {};
char ch[10] = {};
printf("%d\\n", sizeof(arr)); //(1)
printf("%d\\n", sizeof(ch)); //(2)
fun1(arr); //(3)
fun2(ch); //(4)
return 0;
}
(1) (2) (3) (4)的输出分别是多少?
(3)(4)传过去的是地址,而地址的大小在32位操作系统中始终是4个字节
而(1)(2)sizeof(数组的地址)显示的是数组的大小
关系操作符
>
>=
<
<=
!=
==
注意判断条件时"=“和”=="的区别
if(a==3) //检测a是否为3
if(a=3) //把3的值赋给a,此代码相当于 if(3)
在判断关系操作符表达式的时候,满足表达式返回1,否则返回0
零是假,任何非零值皆为真
int oa=a!=0;
if(oa==(b!=0));
// a!=0 返回的是0或1,此表达式只有当a,b都是0,或都不是0时才成立
逻辑操作符
&& 逻辑与
|| 逻辑或
逻辑与/或 与按位与/或
1&2 ------0
1&&2 ------1
1|2 ------3
1||2 ------1
注:当逻辑与第一个为假时,后面的表达式就不用计算了,逻辑与结果为假
同理当逻辑或第一个为真时,后面的表达式也不用计算,逻辑或结果为真
int i=0,a=0,b=2,c=3,d=4
i=a++ && ++b && d++;
a,b,c,d 分别为多少
1,2,3,4
条件操作符
exp1?exp2:exp3
//例如 c=a>b?a:b 等价于
if(a>b)
{
c=a;
}
else
{
c=b;
}
逗号表达式
exp1,exp2,exp3
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式时最后一个表达式的值
下表引用、函数调用和结构体成员
1. [ ]下标引用操作符
操作数:一个数组名+一个引索值
int arr[10]={1,2,3,4,5,6,7,8,9,10};
arr[9]=10;
[]的两个操作数是arr+9
2. ()函数操作符
操作数:函数名+传过去的参数
3. 访问一个结构的成员
-> 指针访问结构体成员
. 结构体变量访问结构体
#include<stdio.h>
struct student
{
int age;
}stu;
void fun1(struct student* p)
{
printf("%d", p->age); //指针指向结构体变量
}
void fun2(struct student p)
{
printf("%d", p.age); //结构体变量访问结构体变量
}
int main()
{
struct student atu = { 12 };
fun1(&atu);
fun2(atu);
return 0;
}
表达式求值
表达式求制的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求制=值过程中可能需要转化成其他类型。
隐式转换类型
c的整型的算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换成为整形提升。
总结一句:整形提升只限于整型!且发生在比4个字节小的整型(32位)参加表达式运算时
发生整型提升的条件:
比int类型内存要小的整型才会发生整型提升
整型提升发生的情况:
- 发生表达式的运算 例如:加减 赋值运算符
- 以整型去打印如 %d %u时
为什么要发生整型提升:
char a,b,c;
c=a+b;
a,b的值被提升位普通整型,然后在执行加法运算。 “+”-------------加法运算
加法运算完成之后,结果被截断,最后储存在c中 “=”--------------赋值运算
如何进行整型提升
char a=-1;
// char占八个比特位为11111111 (其实这8位可以理解为是由int a=-1的32位的补码截断得来的)
//高位补符号位后11111111111111111111111111111111
#include<stdio.h>
int main()
{
char c = 1;
printf("%u\\n", sizeof(c)); // 1
printf("%u\\n", sizeof(+c)); //4
printf("%u\\n", sizeof(-c)); // 4只要发生表达式就会发生整型提升
return 0;
}
操作符的属性
复杂表达式的求值有三个影响的因素:
-
优先级
-
结核性
-
是否控制求值顺序
操作符的优先级结核性表:
优先级顺序总结来说从高到低依次为 -
() 括号 () 函数调用 []下表引用 . ->访问结构体成员
-
单目操作符
-
算术操作符
-
移位操作符
-
关系操作符
-
逻辑操作符
-
条件操作符
-
位操作符
-
赋值操作符
-
逗号表达式
我们需要记住的最重要的两点是:
1、任何一个逻辑运算符的优先级低于任何一个关系运算符
2、移位运算符的优先级比算数运算符要低,但比关系运算符要高
《C与指针》对优先级和结合性是这样描述的:两个相邻的操作符哪个先执行取决于他们的优先级,如果两者的优先级相同,那么它们的执行顺序由它们的结合性决定
例如: a*b+c*d+e*f
如果我们只看优先级,执行的顺序可以是
a*b -> c*d -> e*f -> a*b+c*d -> a*b+c*d+e*f
但是这不是唯一的解法,如果我们先看结合性,从左往右依次判断优先性,只要保证乘法在他相邻的加法中先运算就可以了
(a*b) -> (c*d) -> (a*b)+(c*d) ->e*f -> {(a*b)+(c*d)}+(e*f )
再例如:g+h+e*f
按照结合性第一个加号比第二个加号先算,按照优先级乘号比第二个加号先算,但是第一个加号和乘号谁第一个算我们就不得而知了,但唯一确定的是第二个加号是最后一个运算的。于是就产生了许多歧义代码
如:int i=1; a=(++i)+(++i)+(++i) // 类比g+h+e*f
如果是最后一个++先算,那就是4+4+4=12
如果是+号先算,那就是3+3+4=10
如:c+--c
按优先级--的优先级大于+,但是+号左边的数是在++前获取的还是++后获取的
个人理解:优先级是时间上发生的顺序,而结合性是空间上的方向,同一个表达式中有不同的计算路径,但如果前面表达式的值影响后面表达式的值,就会发生歧义
以上是关于操作符详解的主要内容,如果未能解决你的问题,请参考以下文章
14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段