操作符学习总结

操作符学习总结

一、操作符分类

(1)算术操作符

(2)移位操作符

(3)位操作符

(4)赋值操作符

(5)单目操作符

(6)关系操作符

(7)逻辑操作符

(8)条件操作符

(9)逗号表达式

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

二、算数操作符

 +   -   *   /   %

主要介绍‘%’ 。

#include<stdio.h>
int main()
{
	int a = 5 % 2;
	int b = 5 / 2;
	printf("%d\n", a);
	printf("%d", b);
	return 0;
}                       //注意‘%’两边应该是整数

image-20220515144914682

注意:

\1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。

\2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

\3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

三、移位操作符

<< //左移操作符
>> //右移操作符
    
//注:移位操作符的操作数只能是整数。
//    移动的是二进制位。    

(一)左移操作符

<<

移位规则:左边抛弃、右边补0

(注意:整数在内存中存的是补码)

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

其中移位和运行情况如下

image-20220515151331454

image-20220515153542148

如果是负数如“-5”

#include <stdio.h>
int main()
{
	int a = -5;
	int b = a << 1;
	printf("%d", b);
	return 0;
}

image-20220515153721583

image-20220515153658749

(二)右移操作符

>>

移位规则:

首先右移运算分两种:

\1. 逻辑移位

左边用0填充,右边丢弃

\2. 算术移位

左边用原该值的符号位填充,右边丢弃

不同编译器下使用的规则不同

#include <stdio.h>
int main()
{
    int a = 5;
    int b = a>>1;
    
    return 0;
}

算术移位:

逻辑移位:

正数是两者右移形式上相同。

如果是负数的情况下:

#include <stdio.h>
int main()
{
    int a = -5;
    int b = a>>1;
    printf("%d",b);
    return 0;
}

可以看出,当前编译器使用算数移位

image-20220515160101408

算数移位背后规则:

image-20220515160044647

警告⚠:

对于移位运算符,不要移动负数位,这个是标准未定义的。

四、位操作符

位操作符有:

这里提到的所有位,都是二进制位。

& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数。

【1】&, 按位与操作符

两位都为1时得到‘1’,只要有一个为0就得到0。

#include <stdio.h>
int main()
{
    int a = -5;
    int b = 3;
    int c = a & b;
    printf("%d", c);
    return 0;
}

image-20220515163052358

注意:这里结果为正数,其补码反码原码三者相同

image-20220515163203583

【2】|,按位或操作符

两者只要有一方为1,结果就为1,两者都为0时,结果为0。

#include<stdio.h>
int main()
{
    int a = -5;
    int b = 3;
    int c = a&b;
    printf("%d",c);
    return 0;
}

image-20220515164021335

【3】^, 按位异或

两者相同得到0,两者相异得到1

#include<stdio.h>
int main()
{
	int a = -5;
	int b = 3;
	int c = a ^ b;
	printf("%d", c);

	return 0;
}

image-20220515164811788image-20220515164841796

注:

a ^ a = 0;
0 ^ a = a;
a ^ a ^ b = a ^ b ^ a = b;
//所以按位异或支持交换律。

五、赋值操作符

(一)=

=

赋值操作符可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。

int a = 0 //此时叫初始话,并不是赋值操作符
a = 10; //这里是赋值操作符
a = x = y+1; //连续赋值,但并不推荐
//这个和
x = y+1;
a = x;
//是一样的

(二)复合赋值符

+=       //a=a+5 和a+=5一样
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

六、单目操作符

单目操作符是只有一个操作数的

!           逻辑反操作(可以将真变为假,假变为真)
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符) (类型)       强制类型转换
    
//C语言中0表示假,非0表示真

注意:

int c = 5;
printf("%d",c--);
printf("%d",c);
//这里的c要先赋值再减,因此输出结果是5,4

七、关系操作符

>
>=
<
<=
!=      //用于测试“不相等”
==      //用于测试“相等”
    //在编程的过程中== 和=不小心写错,导致的错误。

八、逻辑操作符

&&     //逻辑与
||     //逻辑或
    
// && 两边都为真时才为真
// || 两边都为假时才会为假
#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;     //先运算a时,先操作再加,所以a=0,&&左边为假,整个式子为假,则只                                //有a参与运算,则为1,2,3,4
    //i = a++||++b||d++;       //这里a为0,然后运算,b先加后算为非零为真,则无需继续算,则结果为                                //1,3,3,4
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0; }
//程序输出的结果是什么?

九、条件操作符

exp1 ? exp2 : exp3

表达式1如果是真,则运算表达式2;否则运算3。


 if (a > 5)
        b = 3;
else
        b = -3;
//转换成条件表达式,是什么样?

b=(a > 5 ? 3 : -3);
//或者
a > 5? b = 3 : b = -3;

十、逗号表达式

exp1, exp2, exp3, …expN

从左向右依次计算,并且以最后一个表达式结果为最终结果。

int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);

十一、下标引用、函数调用和结构成员

【1】 ‘ [ ] ’ 下标引用操作符

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

如:

#include<stdio.h>
int main()
{
    int arr[10]={0};
    arr[7]=8;
    8[arr]=6;
    printf("%d",arr[7]);
    printf("%d",8[arr]);
    return 0;
}

image-20220515184322816

【2】函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

#include<stdio.h>
int Add(int x, int y)
{
    return x+y;
}
int main()
{
    int a = 10;
    int b = 5;
    int c = Add(a,b); //()就是函数调用操作符,操作数为 Add,a,b
                      //函数调用时至少要有一个操作数,即函数名。
    return 0}

【3】 访问一个结构的成员

struct Stu
{
    char name[20];
    int age;
    double score;
};
void set_stu(struct Stu ss)
{
    strcpy(ss.name , "zhangsan"); //这里不能直接赋值,因为name数组名是一个地址,不能直接放字符串
    ss.age = 20;
    ss.score = 100.0;
}
void print_stu(struct Stu ss)
{
    printf("%s %d %lf\n", ss.name, ss.age, ss.score);
}
int main()
{
    struct Stu a = { 0 };
    set_stu(a);
    print_stu(a);
    return 0;
}

这样运行出现了问题是因为实参和形参的关系,应该改成指针

#include <stdio.h>
#include<string.h>
struct Stu
{
    char name[20];
    int age;
    double score;
};
void set_stu(struct Stu *ps)
{
    strcpy((*ps).name , "zhangsan"); //这里不能直接赋值,因为name数组名是一个地址,不能直接放字符串
    (*ps).age = 20;
    (*ps).score = 100.0;
    //strcpy(ps->name,"zhangsan");
    // ps->age = 20;
    // ps->score = 100.0;
    //
    //结构体指针->成员
    //结构体对象. 成员     两者等价
}
void print_stu(struct Stu ss)
{
    printf("%s %d %lf\n", ss.name, ss.age, ss.score);
}
int main()
{
    struct Stu a = { 0 };
    set_stu(&a);
    print_stu(a);
    return 0;
}

image-20220515191221680

十二、表达式求值

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

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

(一) 隐式类型转换

C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

如何进行整体提升呢?

整形提升是按照变量的数据类型的符号位来提升的

//比如‘-1’
char a = -1;
//对于 int a = -1,这里面共有32个比特位
//原码 10000000 00000000 00000000 00000001
//反码 11111111 11111111 11111111 11111110
//补码 11111111 11111111 11111111 11111111char a = -1;//其中只有8个比特位空间
//补码11111111
//因为最高位是1,所以整形提升时候全部补上最高位的数字
//11111111 11111111 11111111 11111111

举例1

int main()
{
	char a = 25;
	//截断后
	//00011001
	char b = 125;
	//截断后
	//01111101

	char c = a + b;
	//00011001 - a
	//01111101 - b
	//提升
	//00000000 00000000 00000000 00011001
	//00000000 00000000 00000000 01111101
	//相加并截断
	//10010110
	//提升(最高位是1)
	//11111111 11111111 11111111 10010110(补码)
	//11111111 11111111 11111111 10010101(反码)
	//10000000 00000000 00000000 01101010(原码)
	// 转为十进制为-106
	//
	printf("%d\n", c);
	return 0;
}

举例2

int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;
 if(a==0xb6)
 printf("a");
 if(b==0xb600)
 printf("b");
 if(c==0xb6000000)
 printf("c");
 return 0; 
}

image-20220515195021975

这里只会打印出‘c’,是因为a,b两者都要进行整型提升,导致不相同;

如果在char和short前都加上unsigned,那会使a和b都成为无符号,无符号整形提升,高位补0,那十进制结果不会有改变,这时会打印a , b , c三者。

举例3

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	return 0;
}

image-20220515195539929

出现这种情况是因为后两者c都放入表达式,一旦进入表达式,就会整型提升。

(二)算数转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

这个表格是向上转换,比如int 和 float 相遇,会转化为float

(三)操作符的属性

复杂表达式的求值有三个影响的因素。

\1. 操作符的优先级

\2. 操作符的结合性

\3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

一些有问题的表达式:

【1】

a*b + c*d + e*f

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

这里最好给每一个项加上括号。

【2】

c + --c;

同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是并没有办法得知, +操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

int c = 2;
//如果c先获取,那运算为:2+1
//如果c后获取,那运算为:1+1

【3】

int fun()
{
     static int count = 1;
     return ++count; 
}
int main()
{
     int answer;
     answer = fun() - fun() * fun();
     printf( "%d\n", answer);//输出多少?
     return 0; 
}

上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。

但是函数的调用先后顺序无法通过操作符的优先级确定。

【4】

#include <stdio.h>
int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0; }

这个代码在不同环境编译器下结果不同,因此也具有问题


最近一直忙,没有时间更新博客,今天花了一个下午终于写出来了,请各位大佬指导!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值