操作符的定义与介绍

本文详细介绍了C语言中的操作符分类,包括算数操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式以及结构成员的访问。同时,讲解了表达式的隐式类型转换、算术转换和操作符的属性,如优先级和结合性。内容涵盖了操作符的使用示例和计算过程,以及表达式求值的相关规则。
摘要由CSDN通过智能技术生成

目录

操作符的分类

  一、算数操作符

 二、移位操作符

三、位操作符 

 四、赋值操作符

五、单目操作符 

 六、关系操作符

七、逻辑操作符

 八、条件操作符

 九、逗号表达式

 十、下标引用操作符、函数调用操作符和结构成员

二.表达式的求值

 一.隐式类型转换

 二.算术转换

 三.操作符的属性


操作符的分类

  一、算数操作符

难度不大通过简单的代码来介绍 

    float a = 3.0 / 2;  //得到的是商
	printf("%0.2f", a);

	int b = 9 % 2; //得到的是取模 左右两边必须为整型 取值范围 如 m%n(0~n);
	printf("%d", b);

 二、移位操作符

<< 表示左移

>> 表示右移 

这里面的移位都是在二进制的方向上考虑的 所以必须先知道二进制 操作的二进制位

    int a = 5;
	int b=a << 1;  //(不能移动负数位置)
	printf("%d\n", a);
	printf("%d\n", b);

 代码解释

整数的二进制表示有3种形式:
1.原码 2.反码 3.补码
正整数的 原码、反码、补码 是相同的
负整数的 原码、反码、补码 是要计算的

 例子:

int a = 5;
二进制形式为 101 因为a是个整型,所以应该有4个字节32个比特位
所以要存放a的时候应该有三十二个位置 即:00000000 00000000 00000000 00000101
则如果是有符号位的时候 00000000 00000000 00000000 00000101 这里面的最高位:0 代表的是符号位
符号位:0 表示整数 1 表示负数
则对于正整数 a 的 原码、反码、补码都相同 均为:00000000 00000000 00000000 00000101、如果 a = -5
则其原码:10000000 00000000 00000000 00000101 (-5的原码)
反码:符号位不变,其他位 按位取反:11111111 11111111 11111111 11111010 (-5的反码)
补码:反码加一 11111111 11111111 11111111 11111011 (-5的补码)
//****整数在内存中存储的是补码的二进制序列****

回到代码中 

//a<<1 的解释 a向左移动1位  :00000000 00000000 00000000 00000101 (5的补码,内存中以补码形式存储)
	//00000000 00000000 00000000 00000101  -->  0000000 00000000 00000000 00000101 0 向左移动一位,左边丢了一位,右边补上一位0
	//则 a = 5 b = 10   a本身是不变的
	//****打印出来的是原码****
	//****左边的丢弃,右边的补零  先丢符号位****
	
	int c = -5;
	int d = c << 1;  
	printf("%d\n", c);
	printf("%d\n", d);       //-5 的 原码 10000000 00000000 00000000 00000101
							//反码  11111111 11111111 11111111 11111010 
							
							//补码  11111111 11111111 11111111 11111011      <---  c 在内存中存储的序列
	//则  c<<1 解释:1111111 11111111 11111111 11111011 0   ——> c 左移后的补码
	//需要计算出原码:
	//1.先计算出反码:补码减一 :1111111 11111111 11111111 11111010 1
	//2.反码取反的到原码,符号位不变:1000000 00000000 00000000 000001010   
	// d = -10  c = -5 ;

	//  *     左移有*2的效果    *    (移动一次)

	// 右移
	int e = 5;
	//00000000 00000000 00000000 00000101 原码
	//00000000 00000000 00000000 00000101 反码
	//00000000 00000000 00000000 00000101 补码
	int f = e >> 1;

	//对于右移操作符有两种形式:(用哪一种形式是取决与编译器)
	//1.算术右移 :右边的丢弃 ,左边补原符号位
	//2.逻辑右移 :右边的丢弃, 左边的补0   <——(无论正负)


	printf("%d\n", e);
	printf("%d\n", f);
	//从运行结果来看,此编译器采用的是算数右移

	//  *     右移有/2的效果    *    (移动一次)但并不总是有/2的效果

三、位操作符 

& 按(2进制)位与 //a & b ; 双目操作符
| 按(2进制)位或
^ 按(2进制)位异或 //&n; 单目操作符
他们的操作数必须是整数

 下面通过代码来具体介绍

//& 运算
	int h = 3;
	int g = -5;
	int k = h & g;
	//操作肯定是按照内存中的存储方式进行运算, 内存中是以补码的形式存储
	// h = 3   补码: 00000000 00000000 00000000 00000011
	// g = -5  补码: 11111111 11111111 11111111 11111011 
	//则 k   补码  : 00000000 00000000 00000000 00000011    (与操作 都假才为假,0为真,1为假,)
					//(只要有0就是0,必须同时为1 才为1)

	printf("%d\n", k);  //打印是按照原码打印,手写的时候需要再计算为原码

	// | 运算
	int h1 = 3;
	int g1 = -5;
	int k1 = h1 | g1;
	// h1 = 3   补码: 00000000 00000000 00000000 00000011
	// g1 = -5  补码: 11111111 11111111 11111111 11111011 
	//则 k1   补码  : 11111111 11111111 11111111 11111011
	//(两位只要有1就是1,两位同时为1也为1,必须同时为0才为0)
	printf("%d\n", k1);

	//  ^运算
	int h2 = 3;
	int g2 = -5;
	int k2 = h2 ^ g2;
	printf("%d\n", k2);
	// h2 = 3   补码: 00000000 00000000 00000000 00000011
	// g2 = -5  补码: 11111111 11111111 11111111 11111011 
	//则 k2   补码  : 11111111 11111111 11111111 11111000
	//(对应的二进制位,相同为0,相异为1)

 

 关于异或操作的一个技巧

a^a=0
0^a=a

例题:

//例题:在一组数列中: 1 2 3 4 5 4 3 2 1 找出呢个只出现一次的数
	//解题思路:将其全部异或在一起就行了

	//例题2: 不能创建临时变量(第三个变量),实现两个数的交换
	int a1 = 3;
	int b1 = 5;
	printf("a1=%d b1=%d\n", a1, b1);
	//方法一:加减法
	//a1 = a1 + b1;   
	//b1 = a1 - b1;
	//a1 = a1 - b1;  //潜在的问题:当两个数字过大的时候两个数字加在一起会溢出
	
	//方法二:异或
	 a1 = a1 ^ b1;
	 b1 = a1 ^ b1;
	 a1 = a1 ^ b1;        //用二进制解释  :
	                      //  a1: 011    —> a1= 110   —> b1= 011  —> a1 = 101
	                      //  b1: 101   |        101  |    a1= 110  |
						//或者将a1、b1一个一个带进去翻译
	printf("a1=%d b1=%d\n", a1, b1);

 

	//代码的实现,求一个数在内存当中的二进制数的1的个数
	//例 : 5
	//101        //00000000 000000000 0000000000 00000101
	             //补码一样
	//想要确定5的二进制数中 1 的个数 
	//方法就是, 将 5 &上 1 可以求出5 的二进制数的最后一位是否为1,然后再将5右移后&上1,求得二进制的倒数第二位
	//以此类推  执行32次
	int num = 0;
	scanf("%d", &num);
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (1 == ((num >> i) & 1))
		{
			count++;
		}
	}
	printf("%d ", count);

 四、赋值操作符

 赋值操作符没有什么特别强调的地方

有一些特别要求和例子

    //连续赋值 a=x=y+1; 实则是a变成了y+1
	//复合赋值符
	int a = 10;
	a = a + 2;
	//等价于
	a += 2;   //同理  a>>1 ==> a>>=1。a=a&4 ==> a&=4

五、单目操作符 

 

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

 

struct S
{
	char name[20];
		int age;
};    
int main(){
    // !:C语言中 0 表示假 非零表示真
	// &   取出谁谁在内存当中的地址 取地址操作符
	int  c = 10;
	int* pc = &c;
	int arr[10] = { 0 };
	//&arr;//取出数组的地址,数组的地址应该放在数组的指针中去
	//也可以对结构体类型取地址
	struct S s = {0};
	&s;
	struct S* ps = &s;
	//* 解引用操作符(间接访问操作符)
	//解析:例如上面的 pc ,pc 里面现在存放的是 c的地址  ,若想通过pc找到c是一种间接访问
	//所以
	//*pc;//意思是通过pc里面所存的地址,找到pc所指的对象 *pc其实就是c *pc就是间接访问操作符
	//& 和 * 是一来一回的形式  a == *&a


	//sizeof ,sizeof是操作符不是函数,单位为字节
	//例子:
	int d = 10;
	short ss = 0;
	printf("%d\n", sizeof(ss = d + 2)); 
    //sizeof内部不参与计算,d和2均为整型,大小为四个字节
	//                                    //而 ss 只有两个字节,硬要写成ss=d+2的形式,则会产生截断,大小还是ss的字节大小
	printf("%d\n", ss);  //ss的值不变


	//  ~按位取反
	int e = 0;
	printf("%d ", ~e); //每一位都要取反,包括符号位,而取反后的二进制是在内存当中存放的,表示的是补码,如果想要打印,(手写计算)必须要变成原码



	// 按位与和按位或
    int a1 = 11;
	//将a1变成15
	a1 |= (1 << 2);
	printf("%d\n", a1);
	//再将a1从15变回11则
	a1 &= (~(1 << 2));    //在二进制位中考虑
	printf("%d\n", a1);


	//++ --操作符
	int a = 3;
	int b  = ++a ;// 前置++,先++后使用//a = a +1 ;b=a
	int b  = a++ ;//后置++ ,先使用后++//b=a;a=a+1;
	 --同理

	//强制类型转换,不鼓励去使用
	//例子:
	//int a = 3.14;
	//若想赋值
	int a = (int)3.14; 
	printf("%d", a);
return 0;
}

 六、关系操作符

 >=  <=  ==  !=  >  <

 唯一的要求就是在==里面,两个字符串是不能够用==的,还有结构体
if ("abcdef" == "abcdef") // 这样写的话 比较的不是字符串,而是首字符的地址
{
strcmp - 专门用来比较字符串大小的函数;
比较的是对应位置上字符的大小,不是比较长度、
}

七、逻辑操作符

 没有什么过多的要求,下面用例子来说明原理

  &&   ||
区分按位与和逻辑与
区分按位或和逻辑或
1&2 ----> 0 比较的是二进制位
1&&2----> 1 比较的是真假
1|2 ----> 3
1||2----> 1

 八、条件操作符

 exp1?exp2:exp3

 对exp1进行判断,如果对了就执行exp2否则就执行exp3。

代码例子:

    int a = 3;
	int b = 0;

	int m = (a > b ? a : b);
	printf("%d", m); 

 九、逗号表达式

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

 

    int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//求c 
	printf("%d\n", c); //a>b 对表达式没有任何影响 
	
	if(a=b+1,c=a/2,d>0)  此表达式只以最后一个表达式来判断

 十、下标引用操作符、函数调用操作符和结构成员

 1.[ ]下标引用操作符

 操作数:一个数组名+一个索引值

    int arr[10] = { 0 };
	arr[4] = 5; //[] 下标引用操作符,操作数是arr 4
	//且 arr[4] <==> *(arr+4) <==> *(4+arr) <==> 4[arr]

 2.函数调用操作符

#include<stdio.h>
int ADD(int x,int y)
{
    return x+y;
}
int main()
{
    int ret = ADD(3, 4);//()函数调用操作符,操作数是ADD,4,3但是不能跟下标操作符一样随便换位置
	printf("%d", ret);
	//至少应该有一个操作数
 return 0;
}

 3.访问结构体成员

. 结构体.成员名
-> 结构体指针->成员名

 下面通过代码来具体介绍

#include<stdio.h>
#include<string.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
void print1(struct Stu ss)
{
	printf("%s %d %f\n", ss.name, ss.age, ss.score);
}
void print2(struct Stu* ps)
{
	printf("%s %d %f\n", (*ps).name, (*ps).age, (*ps).score);
	//打印3:
	printf("%s %d %f\n", ps->name, ps->age, ps->score);
}
int main()
{
    struct Stu s = { "张三",20,90.5f };
	//打印1:
	print1(s);
	//打印2:
	print2(&s);
	//打印3: -> 

	//修改
	//s.name = "张三丰"; //直接这样是不行的,因为数组名是一个地址
	// *(s.name)="张三丰"对此解引用也是不行的,因为只能解引用一个字符,所以存放 张三丰 是不行的
	//解决方法1:
	strcpy(s.name , "张三丰");
	//解决方法2:
	scanf("%s", s.name);

	print2(&s);
}

 操作符的内容大概就讲完了

下面是表达式的求值问题

二.表达式的求值

 表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样。有些表达式的操作数在求值的过程中可能需要转换为其他类型。

 一.隐式类型转换

c的整型算数运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为:整型提升

 例子:

char c1 = 3;
	// 00000000 00000000 00000000 00000011
	// 因为c1是char类型的,只能存放8个二进制位,所以要产生截断,存放的是00000011 - c1
	char c2 = 127;
	//同理 01111111 - c2
	char c3 = c1 + c2;
	//所以要计算的时候,要先把c1,c2进行整型提升
	printf("%d\n", c3);//在运算的时候要先转换成整型;
	//如何进行整型提升
	//整型提升是按照变量的数据类型的符号位来提升的 

	//负数整型提升
	//整型提升的时候,高位补充符号位,即为1
	//正数整型提升
	//整形提升的时候,高位补充符号位,即为0
	//所以c1提升后 00000011 -> 00000000 00000000 00000000 00000011
	//                   c2 -> 00000000 00000000 00000000 01111111
	//                   c3 -> 00000000 00000000 00000000 10000010
	//c3里面存放为 10000010
	//c3是char类型的要打印为%d类型,所以
	//要整型提升:11111111 11111111 11111111 10000010 -补码
	//            10000000 00000000 00000000 01111110 -原码            

 二.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行
下面的层次体系称为寻常算数转换
long double
double
float
unsigned int
long int
unsigned int
int
如果一个操作数的类型在上面的排列中排名较低,呢么首先要转换为另一个操作数类型(排名较高的)后再执行运算
要转换合理
float f = 3.14;
	int num = f;//隐式转换,会有精度丢失

 三.操作符的属性

复杂表达式的求值有三个因素:
1.操作符优先级
2.操作符的结合性
3.是否控制求值顺序

 优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。表达式的结合次序取决于表达式中各种运算符的优先级。优先级高的运算符先结合,优先级低的运算符后结合,同一行中的运算符的优先级相同。

 

 

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值