操作符详解

Posted 正义的伙伴啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作符详解相关的知识,希望对你有一定的参考价值。

操作符的类别

在这里插入图片描述

1、算数操作符

+        -        *        /        %

注意事项:

  1. "/"除法运算想要得到的是小数,除数与被除数都必须是小数 例如
float a=4/5; //此时a=0.000000
  1. "%"两边必须是整数

2、移位操作符

    <<左移操作符  >>右移操作符

1.左移操作符

      左边抛弃、右边补0

在这里插入图片描述

2.右移操作符

两种规则:

  1. 算数右移: 右边丢弃,左边补原符号位
  2. 逻辑右移:右边丢弃,左边补0
    标准说明所有无符号整数执行的都是逻辑右移,但对于有符号值到底是采用逻辑移位还是算数右移取决于编译器
    我用的编译器(vs 2019)采用的是算数右移
    我用的编译器(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 分别为多少
 1234

条件操作符

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类型内存要小的整型才会发生整型提升

整型提升发生的情况:

  1. 发生表达式的运算 例如:加减 赋值运算符
  2. 以整型去打印如 %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
按优先级--的优先级大于+,但是+号左边的数是在++前获取的还是++后获取的

个人理解:优先级是时间上发生的顺序,而结合性是空间上的方向,同一个表达式中有不同的计算路径,但如果前面表达式的值影响后面表达式的值,就会发生歧义

以上是关于操作符详解的主要内容,如果未能解决你的问题,请参考以下文章

20160206.CCPP体系详解(0016天)

20160206.CCPP体系详解(0016天)

(转) Java中的负数及基本类型的转型详解

详解Android WebView加载html片段

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段

Python中verbaim标签使用详解