当前位置:网站首页>操作符详解
操作符详解
2022-07-31 12:59:00 【南猿北者】
文章目录
前言
- 各种操作符的介绍。
- 表达式求值
操作符分类
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
一、算术操作符
算术操作符有哪些呢?
+、-、*、/、%
优先级和结合性如何?
都是双目操作符;
注意:
1、%两边的操作数只能为整数,不能为浮点数;
2、=、-、 * 、/ 两边两个操作数都为整数都为整数,才执行整数的运算法则,只要其中有一个浮点数,则执行浮点数运算;
二、移位操作符
移位操作符分为两类:
<<:左移操作符;
>>:右移操作符;
首先我们来了解一下左移操作符;
使用方法:
左移多少位,右边就补多少位0;
操作代码:
int main()
{
int a = -4;
printf("%d\n",a<<4);
return 0;
}
那我们就得知道,所有的整数在内存中都是以补码进行存储的,我们的左右移操作符都是对补码进行直接操作的;
于是我们的了解-4的补码:
既然有了补码,那我们直接对其进行操作:
我们向左移动了4个位,那右边有缺失啊,补啥?无脑补0;
我们现在不就得到一串新的补码,我们在对其进行原码转换:
补码的补码就是原码:
补码:1111111111111111111111111000000
反码:1000000000000000000000111111
补码:1000000000000000000001000000
既:-64;
我们来看看结果对不对呢?
的确是这样:
我们接下来再来看看右移操作符:
使用方法:
对于有符号整型:右移对少位,左边就补多少个原符号位;(算术右移)
对于无符号整型:右移对少位,左边就补多少个0;(逻辑右移)
我们来看段代码:
int main()
{
int a = -4;
unsigned int c = -4;
int b = a >> 4;
int d = c >> 4;
printf("b=%d\n", b);
printf("d=%d\n",d);
return 0;
}
这是-4的补码:
在a,c中都是一样的序列:
a>>4;
左边补原符号位:
补码:11111111111111111111111111111111
反码:1000000000000000000000000000
补码:1000000000000000000000000001
既-1,故b=-1;
再来看c
补码:00001111111111111111111111111111
既:268435455,故d=268435455;
我们来看看是不是
显然是的;
警告 :
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10;
num>>-1;//error
三、 位操作符
主要有:
& :按位与
| :按位或
^ :按位异或
~:按位取反
注:他们的操作数必须是整数。
&运算规则:在相同比特位下:1&0=0;0&0=0;1&1=1;
比如
3:011
4:100
则3&4=000=0;
的确是这样;
|运算规则:在相同比特位下:1|0=1;0|0=0;1|1=1;
3:011
4:100
则3|4=111=7
的确如此:
再来看看^异或运算规则:相同比特位下,相同为0,相异为1;
3:011
4:100
则3^4=111=7;
当然根据这个操作符的规则,我们可以得出这一个公式:
a^a=0;
a^0=a;
根据这个公式我们可以出一道题:
不能创建临时变量(第三个变量),实现两个数的交换:
法一:
int main()
{
int a = 2;
int b = 3;
printf("\nbefore:a=%d b=%d", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("\nafter:a=%d b=%d",a,b);
return 0;
}
这方法有个弊端就是容易造成超出范围,即a,b很大,但是没有超出int范围,但是a+b就超出了,后面一系列操作就会出错;
为了解决这个问题我们可以用^来帮助我们:
法2:
int main()
{
int a = 2;
int b = 3;
printf("\nbefore:a=%d b=%d", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("\nafter:a=%d b=%d",a,b);
return 0;
}
这样不会产生进位,也就不会出现超出范围的说法;
~按位取反运算规则:按比特位逐个取反:1->0;0->1;
为了方便演识,我们写个打印二进制的函数来看看:
void print_bin(int n)//大家可以下来自行研究一下
{
int i = 0;
int num = sizeof(int) * 8;
i = num - 1;
while ((i--)>=0)
{
if (n & (1 << i))
printf("1");
else
printf("0");
}
putchar('\n');
}
int main()
{
int a = -1;
printf("按位取反之前:");
print_bin(a);
printf("按位取反之后:");
print_bin(~a);
return 0;
}
四、 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值。
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20; a = x = y+1;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1; a = x;
这样的写法是不是更加清晰爽朗而且易于调试。
当然还有升级版赋值操作符:
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合形式;
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁
五、 单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
– 前置、后置–
++ 前置、后置++
* 间接访问操作符(解引用操作符)
- (类型) 强制类型转换
逻辑取反:
相同的代码我们可以这么改:
就是在逻辑上是真,!一下就是逻辑上是假了;同理逻辑上是假,!一下逻辑上就是真了;
- – 、 +这两就不用多做解释了吧;
&:取地址,取出的是所有地址中最低的一个地址(对所有类型都是这样)
sizeof经常被认为是函数的关键字;求一个类型在内存中占的空间大小;
虽然用法上与函数并无差异,但是它确实不是一个函数,下面前我们通过例子来证明:
我们可以发现sizeof 变量名是可以编译运行的,也是可以不用带括号的,也就是括号可以被省略,但是函数的括号确实不能被省略,从这一点我们证明了sizeof不是函数,是一个关键字,但是在对sizeof(类型)必须加括号,不带编译器会报错;
– 前置、后置–
++ 前置、后置++
这个的却别就是,后置- - 、++先使用后自增,如果有接收方的话,则会被先接受,在自增:
前置- -、++先自增在使用,有接受方,接收方接收的是自增过后的值,如没有,直接自增后置也是如此;
*解引用操作符:
通过指针的方式间接访问变量;
(强制类型转换):
只是改变编译器看待数据的角度,并不会该变其内存中的二进制序列;
int a=(int)3.13;
其实最终a=3;,但是3.13在内存中二进制布局并没有改变;
六. 关系操作符
=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
警告:
在编程的过程中== 和=不小心写错,导致的错误
七、逻辑操作符
逻辑操作符有哪些:&&逻辑与 ||逻辑或
区分逻辑与和按位与
区分逻辑或和按位或
1&2----->0
1&&2---->1
1|2----->3
1||2---->1
对于&&来说,如果左边为假,编译器就不会再去执行右边了,在编译器看来这没必要;
对于||来说,如果左边为真,编译器就不会再去执行右边了,在编译器看来这没必要;
//360笔试题
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
//i = a++||++b||d++;//这个读者下来可以自己测试一遍;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0; }
//程序输出的结果是什么?
归最终结果就是上式;
八、 条件操作符
1.
if (a > 5)
b = 3;
else
b = -3;
转换成条件表达式,是什么样?
(a>5)?3:-3;
九、 逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);/
c=13;
十、下标引用、函数调用和结构成员
- [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值 - ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //实用()作为函数调用操作符。
test2("hello bit.");//实用()作为函数调用操作符。
return 0;
}
- 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu) {
stu.age = 18; }
void set_age2(struct Stu* pStu) {
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;//结构成员访问
stu.age = 20;//结构成员访问
set_age1(stu);
pStu->age = 20;//结构成员访问
set_age2(pStu);
return 0; }
十一、 表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
整型提升?
C的整型算术运算总是至少以默认整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purpose CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU,C的整型算术运算总是至少以默认整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这个转换称为整型提升。整型提升的意义:去执行运算。
总的来说,只要是小于int型的整型数据在进行整数运算时,都会先提升称为int类型在进行运算;(比如char,unsigned char,short,unsigned short在进行整数运算时都会发生整型提升)
如何进行整型提升?
对于有符号整数来说:
往最高位补原符号位;
比如char a=-1;
在char中:11111111
发生整型提升就是,补符号位,符号位为1,则补1;
11111111111111111111111111111111;
对于无符号来说,直接补0
比如unsigned char a=-1;
11111111:
发生整型提升:
00000000000000000000000011111111;
整型提升的例子;
int main()
{
char a = 0xb6;//10110110
short b = 0xb600;//1011011000000000
int c = 0xb6000000;
if(a==0xb6)//会发生整型提升:
//补码:11111111111111111111111110110110
//反码:10000000000000000000000001001001
//补码:10000000000000000000000001001010//-4A
printf("a");
if(b==0xb600)//会发生整型提升
//补码:1111111111111111101101100000000
//反码:1000000000000000010010011111111
//补码:1000000000000000010010100000000//-2500
printf("b");
if(c==0xb6000000)//不会发生整型提升
printf("c");
return 0; }//最终结果只会打印c
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0; }
十二、算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
警告:
但是算术转换要合理,要不然会有一些潜在的问题。
float f = 3.14;
int num = f;//隐式转换,会有精度丢失
边栏推荐
- Using SQL Server FOR XML and FOR JSON syntax on other RDBMSs with jOOQ
- 行业案例 | 全面防护 赛宁助力能源工控安全建设
- Character Functions and String Functions
- Selenium自动化测试之Selenium IDE
- go中select语句
- PyQt5 rapid development and actual combat 9.7 Automated testing of UI layer
- 全动力学约束的机器人高效时间最优轨迹规划
- Getting started with jmeter performance testing steps (performance testing tool jmeter)
- 手撕Verilog PWM呼吸灯
- ERROR 2003 (HY000) Can‘t connect to MySQL server on ‘localhost3306‘ (10061)解决办法
猜你喜欢
Ali on three sides: MQ message loss, repetition, backlog problem, how to solve?
【OpenCV】-边缘检测汇总示例
NameNode (NN) and SecondaryNameNode (2NN) working mechanism
函数的参数
centos7安装mysql5.7
IDEA版Postman插件Restful Fast Request,细节到位,功能好用
C# 中的Async 和 Await 的用法详解
Architecture Camp | Module 8
CentOS7 —— yum安装mysql
centos7安装mysql5.7步骤(图解版)
随机推荐
IDEA版Postman插件Restful Fast Request,细节到位,功能好用
尚硅谷–MySQL–基础篇(P1~P95)
FastAPI encapsulates a generic response
查看Oracle数据库的用户名和密码
攻防演练丨赛宁红方管控平台走进广东三地 助力数字政府网络安全建设
一文吃透哈希表
IDEA的database使用教程(使用mysql数据库)
0x80070570 The file or directory is damaged and cannot be deleted (how to delete 0x80070091)
集群的安全模式
Hard disk partition, expand disk C, no reshipment system, not heavy D dish of software full tutorial.
Use IN List Population in Your JDBC Application to Avoid Cursor Cache Contention Issues
Adding data nodes and decommissioning data nodes in the cluster
全局平均池化层替代全连接层(最大池化和平均池化的区别)
IDEA连接MySQL数据库并执行SQL查询操作
中望3D 2023正式发布,设计仿真制造一体化缩短产品开发周期
Centos7 install mysql5.7 steps (graphical version)
如何使用StarUML画类图[通俗易懂]
The function of SQL GROUP BY dependence
带有对称约束切换线性系统的结构可控性
C#使用ComboBox控件