当前位置:网站首页>C语言操作符详解

C语言操作符详解

2022-08-11 07:38:00 天黑再醒

目录

一.操作符的分类:

1.算术操作符 

2.移位操作符

 3.位操作符

 4.赋值操作符

5.单目操作符 

 6.关系操作符

7.逻辑操作符 

8.条件操作符 

9.逗号表达式 

 10.下标引用、函数调用和结构成员

二.表达式求值

1.隐式类型转换 

 2.算术转换

 3.操作符的属性

4.一些问题表达式


一.操作符的分类:

1.算术操作符
2.移位操作符
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.下标引用、函数调用和结构成员

1.算术操作符 

+  -   *  /   %

%(取余)         结果是余数。

%操作符的两个操作数必须是整数,则最后返回的数是余数。

其余的操作符可以是整数也可以是浮点数。

对应   /  操作符如果两个都是整数,则执行整数除法。

2.移位操作符

<< 左移操作符
>> 右移操作符
这个移位是指在二进制的基础上进行的。
而整数的内存的存储是补码的二进制数

如:

int   a=5;

 这个5是十进制数,转化二进制就是101,又因为a是整数,占4个字节,32个bit位,则:

原码:00000000 00000000 00000000 00000101

反码:00000000 00000000 00000000 00000101

补码:00000000 00000000 00000000 00000101

(正整数的原码,反码,补码都是一致的)

若:

int a=-5;

原码:10000000 00000000 00000000 00000101(把5的原码首位改成1)

反码:111111111 111111111 111111111  11111010(-5原码首位不变,其余的取反)

补码:111111111 111111111 111111111  11111011  (在反码的基础上+1)

(负数的原码,反码,补码各不相同)

(原码,补码,反码的首位称为符号位 ,符号位为0 -代表正数,1-代表负数)

int a = 5;

int b <<1 ; 

b的值是a的补码向左移动一格,即:左边去掉一个数,右边补一个0;

b的补码:00000000 00000000 00000000 00001010

转化二进制数就是10

同理:

int a = 5 ;

int c >> 1 ;

c的值是a的补码向右移动一格,即:右边去掉一个数,左边补一个0;

c的补码:00000000 00000000 00000000 00000010

转化二级制就是2

从算术上说:左/右边丢弃,右/左边补原符号位

从逻辑上说:左/右边丢弃,右/左边补0

移位不会改变操作数的本身数值

注:移位操作符的操作数只能是整数。
对应于移位操作符,不要移动负正数。

#include<stdio.h>
int main()
{
	int a = 5;
	int b = a << 1;
	int c = a >> 1;
	printf("%d\n", a);     // 5
	printf("%d\n", b);     //10
	printf("%d\n", c);     //2
	return 0;
}

 3.位操作符

&    //按(二进制)位与      
|     //按(二进制)位或
^    //按(二进制)位异或
#include<stdio.h>
int main()
{
	int a = 3;
	int b =-5;
	int c = a & b;
	
	printf("%d\n", c);
	return 0;
}

 先分别求出a和b的补码,&是与,二级制数一一对应,两个同为1则为1

 先分别求出a和b的补码,|是或,二级制数一一对应,两者其中为1则为1

 先分别求出a和b的补码,^是异或,二级制数一一对应,两者相同则为0,相异为1

  不能创建临时变量(第三个变量),实现两个数的交换。

#include<stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	printf("%d %d\n", a, b);
    //这是有第三个变量的
	//int tmp = a;
	//a = b;
	//b = tmp;
	//printf("%d %d\n", a, b);
    //这是没有第三方变量

	//a = a + b;        //把a和b的和赋值给a
	//b = a - b;        //此时a=8;b=a-b;b=3
	//a = a - b;        //此时a=8,b=3;a=a-b;a=5
	//printf("%d %d\n", a, b);
    //但这种有缺陷,当a,b的数值过大,导致溢出,就会出现问题
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("%d %d\n", a, b);
    //a=3;  二进制是011
    //b=5;  二级制是101
    //进行异或后就成立了
	return 0;
}

注意:异或操作符只能运用整数里。

 4.赋值操作符

=   赋值操作符可以连续使用,从右向左赋值,但不能在定义变量时对变量进行连续赋值。

复合赋值:

  1. +=

  2. -=

  3. *=

  4. /=

  5. %=

  6. >>=

  7. <<=

  8. &=

  9. |=

  10. ^=

5.单目操作符 

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转换
//*&a==>a

sizeof对于变量名可以省略后面的括号,但是对于数据类型不能省。

sizeof括号中放的表达式不参与运算。

EOF-->-1,则~EOF是0

前置++是先++再使用

后置++是先使用再++

 

 6.关系操作符

>
>=
<
<=
!=       用于测试“不相等”
==      用于测试“相等

7.逻辑操作符 

&&       逻辑与
||          逻辑或

逻辑与,逻辑或只用来关心真假,按位与,按位或是用来二进制的计算。

逻辑与是两者都为真才行,逻辑或是有一个成立就可以。

#include <stdio.h>
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=0;a++是后置,先使用,a++是0,所以为假,后面的不用看,最后打印出a=1,b=2,c=3,d=4。

8.条件操作符 

exp1 ?exp2 : exp3

exp1如果为真,exp2执行

exp1如果为假,exp3执行

9.逗号表达式 

exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

 10.下标引用、函数调用和结构成员

1.   [ ]   下标引用操作符
操作数:一个数组名 + 一个索引值
 int arr[10];
 arr[9] = 10;
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
int add(int x,int y)
{
  return x+y;
}
int main()
{
int c=add(2,3);
printf("%d",c);
return 0;
}
3. 访问一个结构的成员
   结构体.成员名
结构体指针->成员名
 
#include <stdio.h>
struct stu
{
	char name[20];
    char id[20];
	int age;
};
int main()
{
	struct stu b = {"xiaohong","C202115311103",55};
	printf("%s %s %d",b.name,b.id,b.age);
	struct stu * pb=&b;
	printf("%s %s %d",(*pb).name,(*pb).id,(*pb).age); 
	//printf("%s %s %d", pb->name, pb->id, pb->age);
}

二.表达式求值

表达式求值的顺序一部分是由操作符的优先级结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

1.隐式类型转换 

C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPUgeneral-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

 怎么整型提升呢?

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
#include<stdio.h>
int main()
{
	char a = 3;
	//3的二进制补码是00000000000000000000000000000011
	//提升后00000011
	char b = 127;
	//127的二进制补码00000000000000000000000001111111
	//提升后01111111
	char c = a + b;
	//00000000000000000000000000000011
	//00000000000000000000000001111111
	//00000000000000000000000010000010
	//c提升后10000010
	//因为 char 为有符号的 char所以整形提升的时候,高位补充符号位,即为1提升之后的结果是:
	//11111111111111111111111110000010
	//11111111111111111111111110000001
	//10000000000000000000000001111110
	//即-126
	printf("%d\n", c);
	return 0;
}

 2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
型,否则操作就无法进行。下面的层次体系称为寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
:但是算术转换要合理,要不然会有一些潜在的问题。

 3.操作符的属性

1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
*两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

优先级: (优先级从上到下依次变小)

操作
名称
用法示例
结合
是否控制求值顺序
()
聚组
(表达式)
()
函数调用
rexprexp...,rexp
从左到右
[ ]
下标引用
rexp[rexp]
.
访问结构成员
lexp.member_name
->
访问结构指针成员
rexp->member_name
++
后缀自增
lexp ++
--
后缀自减
lexp --
!
逻辑反
! rexp
从右向左
~
按位取反
~ rexp
+
单目,表示正值
+ rexp
-
单目,表示负值
- rexp
++
前缀自增
++ lexp
--
前缀自减
-- lexp
*
间接访问
* rexp
&
取地址
&lexp
sizeof
取其长度,以字节表示
sizeof rexp sizeof()
(类型)
(类型)
(类型) rexp
*
乘法
xp * rexp
从左向右
/
除法
rexp / rexp
%
取余rexp % rexp
+
加法rexp + rexp
-减法rexp - rexp
<<
左移位
rexp << rexp
>>
右移位
rexp >> rexp
>
大于
rexp > rexp
>=
大于等于
rexp >= rexp
<
小于
rexp < rexp
<=
小于等于
rexp <= rexp
==
等于rexp == rexp
!=
不等于rexp != rexp
&
位与
rexp & rexp
^
位异或
rexp ^ rexp
|
位或
rexp | rexp
&&
逻辑与
rexp && rexp
||
逻辑或
rexp || rexp
? :
条件操作符
rexp ? rexp : rexp
=
赋值
lexp = rexp
从右向左
+=
...
lexp += rexp
-=
...
lexp -= rexp
*=
...
lexp *= rexp
/=
...
lexp /= rexp
%=
...取模
lexp %= rexp
<<=
...左移
lexp <<= rexp
>>=
...右移
lexp >>= rexp
&=
...
lexp &= rexp
^=
...异或
lexp ^= rexp
|=
...
lexp |= rexp
逗号lexp,rexp从左向右

4.一些问题表达式

示例1:

a*b + c*d + e*f
代码1在计算的时候,由于*+的优先级高,只能保证,*的计算是比+早,但是优先级并不
能决定第三个*比第一个+早执行。
a*b 
c*d 
a*b + c*d 
e*f 
a*b + c*d + e*f
或者:
a*b 
c*d 
e*f 
a*b + c*d 
a*b + c*d + e*f

 示例2:

c + --c;
同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得
知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
如:如果已知c的值为2      先--c得1      2+1=3
如果c的值还没准备好,先--c 得出值    (--c的值)+  --c     比如1+1=2
所以:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
原网站

版权声明
本文为[天黑再醒]所创,转载请带上原文链接,感谢
https://blog.csdn.net/jk1206/article/details/125797701