【C语言】操作符详解

目录

一、算数操作符

 二、移位操作符

1.左移操作符

2.右移操作符

(1) 逻辑右移

(2) 算术右移

 (3)小总结

三、位操作符

四、赋值操作符

五、单目操作符

六、关系操作符

七、逻辑操作符

八、 条件操作符

九、逗号表达式

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

1. [ ]下标引用操作符

 2.  ( )函数调用操作符

3. 访问一个结构的成员

十一、表达式求值

1.隐式类型转换

2.算术转换 

3.操作符的属性

十二、操作符优先级


一、算数操作符

+    -    *   /   %

  • 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数
  • 对于 / 操作符如果两个操作数都为整数,执行整数除法。但是,只要有浮点数参与运算就是浮点数除法
  • % 要求两个操作数必须为整数,得到的是余数

 二、移位操作符

>>        右移操作符

<<        左移操作符

注意:移位操作符的操作数只能是整数

1.左移操作符

规则巧记:左边抛弃,右边补0

这里假设存的是 int n =15;的二进制 进行左移 n<<1; 如果n 在没有被左移赋值的情况下,n自身的值是不会发生变化。(这里与自增自减不一样)

在深层次看一下,一个数 n<<1,  发现相当于该数 n * 2^1。n << 2;   等于 n * 2^2 

2.右移操作符

(1) 逻辑右移

移位规则:右边舍弃,左边补0

假设内存 存放的是 -1 补码的二进制   

(2) 算术右移

移位规则:右边舍弃,左边用原值的符号位进行填充

 

 (3)小总结

如果 算术右移与 逻辑右移 总是分不清。巧记:(算术右移,可以想象成 算数,既然算数肯定会有正负,进而想到 左边填充的是原符号位)

注意:对于位操作符,不存在移动负数位,C语言标准并未规定

int num = 10;

num>> -1;  //error(不要多次一举)

num  << 1;  // ok  右移  -1  这不相当于 左移 1 嘛。

三、位操作符

&        //按位与  巧记:有0则0,其中  一个数 a&1 可求 该数的每一个二进制位

|         //按位或   巧记:有1则1

^        //按位异或 巧记:相同为0,相异为1

注:操作数必须是整数

【例】1

#include<stdio.h>int main() 
{int a = 1;//0001int b = 2;//0010/** a& b;* 0001* 0010* 0000* * a|b* 0001* 0010* 0011* * a^b* 0001* 0010* 1100*/return 0;
}

接下来看一道面试题

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

解法一:

两个数进行来回加减来进行两个数的交换,通过调试的监视的窗口我们可以看到两个数的交换 

 解法二:

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

从打印结果可以看到a 与 b的值进行了交换

【解析】^ 按位异或 相同为0,相异为1(可以理解为 相同假,相异为真)

前面,提到 可以用a&1 该数的每一个二进制位,看下方例题

【例】编写代码,求一个整数存储在内存中的二进制中1的个数

#include<stdio.h>
int main()
{int n = 10;int count = 0;//  0000 1010//  0000 0001int i = 0;for (i = 0; i < 32;i++){if (n & 1 == 1){count++;}		n = n>>1;}printf("%d",count);return 0;
}

 这里用到了 >> 和 ^

解法二:

#include<stdio.h>
int main()
{int n = 10;int count = 0;while (n) {if (n%2 == 1){count++;}n /= 2;}return 0;
}

这里的思路是 因为计算机存储是二进制 0 和1 ,%2取余判断是否为1,/2进行下一位 

 解法三:

#include<stdio.h>
int main()
{int n = -1;int i = 0;int count = 0;while (n){count++;n = n & (n - 1);//1111 1111 1111 1111 1111 1111 1111 1111//1111 1111 1111 1111 1111 1111 1111 1110//1111 1111 1111 1111 1111 1111 1111 1110}printf("%d ", count);return 0;
}

这里的优化 是借助 两个数差1 进行按位与,一个一个位找1

四、赋值操作符

 =  这是赋值符,不是等于!!!

赋值操作符  给一个变量进行赋值 注意赋值操作符的优先级比较低(包括复合赋值符)

int age = 18;age = 20;  //对变量进行赋值

赋值操作符支持连续使用;

int a = 0;
int b = 10;
int c = 1;
a = b = c+1;  //这里是连续赋值

虽然可以连续赋值,但是代码的可读会下降

b = c+1;
a = b;
//这样子是不是看着更用以理解

代码的可读性也是很重要的

接下来看复合赋值符都有哪些

+= 、-=、*= 、/= 、%= 、>>=  、<<= 、&= 、 |= 、^=

int a = 10;
a = a+10;
//可以写成这样
a += 10;

其他复合赋值符同上方用法一致

五、单目操作符

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

 sizeof ()括号里面不是类型时可以省略(),说到sizeof ,其中 size_t 是一种类型,是一种无符号整型的,size_t 就是为sizeof专门设计的一种类型,打印时使用%zd,但是size_t 在不同平台下可能是 unsigned int   也有可能是  unsigned long long int 

sizeof(数组名) ,计算的是整个数组的大小

++前置,先对该数+1,然后再去使用这个数

++后置,相当于 先使用该数,然后在对该数进行 +1

-- 与上方的++同理

六、关系操作符

> 、 >= 、 < 、 <=  、!= 、==

注意:== 这个是 等于

            =   这个是 赋值

七、逻辑操作符

&&        逻辑与

||           逻辑或

要区分

& 与 &&   ;   | 与 ||

1&2   --->  0  

1&&2  --->  1   (左边为假,右边无需计算,直接为假)

1  |  2 --->  3

1  ||  2 ---> 1 (左边为真,右边无需计算,直接为真)

【例】1笔试题,求结果输出的值

#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 = 1 b = 2 c = 3 d = 4

解析    首先 a = 0 ,a++ ,是先使用后 +1,又有&& (左边为假,右边无需计算,直接为假)

打印的是 a  = 1,其他值正常打印

【例】2 对这个题进行改编 a = 1

#include<stdio.h>
int main()
{int i = 0, a = 1, 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 = 2 b = 3 c = 3 d = 5

解析  a = 1时,左边为真,后面表达式继续进行计算,进而 a = 2, b = 3,c =3 ,d= 5

【例】3 对这个题进行改编 && 改为 ||  , 并求出i 的值

#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 = 1 b = 3 c = 3 d = 4  i = 1

解析 因为时逻辑或 (左边为真,右边无需计算,直接为真)虽然先使用 a 为假,但接下来的操作数 ++b , b = 3为真 进而后面无需计算。而 i 表达式里面有真 则 i = 1, 为真

八、 条件操作符

表达式1 ?表达式2 :表达式3;

唯一 一个三目操作符

在这里 看一个 求最大值的代码

 a>b?a:b; //中文解释 a>b 吗?大于就是a, 不大于就是b

【例】求三个数的最大值 

#include<stdio.h>
int main() 
{int a = 1;int b = 2;int c = 3;int max = 0;max = a > b ? a : b;max = max > c ? max : c;printf("%d",max);return 0;
}

 这里是使用两次条件操作符进行计算三个数中的最大值

九、逗号表达式

表达式1 , 表达式2 ,... , 表达式n

逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式

c 的结果是 13

逗号表达式可以对代码进行优化

a = get_val();
count_val(a);
while (a > 0)
{//业务处理a = get_val();count_val(a);
}//使用逗号表达式可以改写为
while (a = get_val(), count_val(a), a > 0)
{//业务处理
}

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

1. [ ]下标引用操作符

注意 操作数为 一个数组名 + 一个索引值

int arr[20]; //创建数组
// [ ] 的操作数 是 arr  和 20

 2.  ( )函数调用操作符

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

#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;
}

3. 访问一个结构的成员

.        结构体.成员名

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

#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;
}

十一、表达式求值

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

1.隐式类型转换

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

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU(general-purpose CPU)是难以直接实现两个(8bit)字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。

//例
char a,b,c;
a = b + c;

在上述代码中,b和c的值被提升为普通整型(int),然后再执行加法运算,加法运算完成之后,结果将被截断,然后再存储于a中

整型提升

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

char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001

无符号整形提升,高位补0

【例】1

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;
}

【结果】c

实例的a,b要进行整型提升,但是c不需要整形提升a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真.

【a】1101 0110  整型提升1111 1111 1111 1111 1111 1111 1101 0110

【b】1101 0110 0000 0000 整型提升1111 1111 1111 1111 1101 0110 0000 0000

【例】2

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

【结果】1 4 4

实例2中的,c只要参与表达式运算,就会发生整型提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节。表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

2.算术转换 

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

long double

double

unsigned long int 

long int

unsigned int

int

向上转换  如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算

注意: 算数转换要合理,否则可能会出现精度丢失
 

float pi = 3.14;​​​​​​​int num = f;//num = 3 精度丢失

3.操作符的属性

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

  • 操作符的优先级
  • 操作符的结合性
  • 是否控制求值顺序

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

十二、操作符优先级

点击下方链接查看​​​​​​​

链接:操作符优先级

注意:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/59883.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度解读|一站式ABI平台 Smartbi Insight V11 能力再升级

纵观过去&#xff0c;我们发现汽车和BI的发展有异曲同工之妙。 100来年&#xff0c;汽车的动力从蒸汽到燃油再到新能源&#xff0c;汽车的操控方式从手动到自动再到智能无人驾驶。而在BI领域&#xff0c;自1958年BI的概念提出后&#xff0c;底层数据准备从报表开发、Cube多维模…

搭建本地开发服务器

搭建本地开发服务器 :::warning 注意 在上一个案例的基础上添加本地开发服务器&#xff0c;请保留上个案例的代码。如需要请查看 Webpack 使用。 ::: 搭建本地开发服务器这一个环节是非常有必要的&#xff0c;我们不可能每次修改源代码就重新打包一次。这样的操作是不是太繁琐…

Docker的入门与使用

什么是Docker&#xff1f; docker官网 简介与概述 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#x…

Java # Spring(2)

一、Spring事物 一、分类 编程式事物&#xff1a;代码中硬编码&#xff08;不推荐使用&#xff09; 声明式事物&#xff1a;配置文件中配置&#xff08;推荐使用&#xff09; 分类&#xff1a; 基于xml的声明式事物基于注解的声明式事物 二、隔离级别 ISOLATION_DEFAULT&…

webpack基础知识六:说说webpack的热更新是如何做到的?原理是什么?

一、是什么 HMR全称 Hot Module Replacement&#xff0c;可以理解为模块热替换&#xff0c;指在应用程序运行过程中&#xff0c;替换、添加、删除模块&#xff0c;而无需重新刷新整个应用 例如&#xff0c;我们在应用运行过程中修改了某个模块&#xff0c;通过自动刷新会导致…

【2.1】Java微服务: Nacos的使用

目录 Nacos介绍 Nacos安装 下载和安装 修改端口 启动 服务注册与发现 导入Nacos管理依赖 导入服务依赖 配置Nacos的服务地址 启动服务&#xff0c;查看已注册的服务 服务分级存储模型 分级存储模型介绍 具体结构 配置实例集群 同集群优先的负载均衡策略 服务权重配置…

【STL】优先级队列反向迭代器详解

目录 一&#xff0c;栈_刷题必备 二&#xff0c;stack实现 1.什么是容器适配器 2.STL标准库中stack和queue的底层结构 了解补充&#xff1a;容器——deque 1. deque的缺陷 2. 为什么选择deque作为stack和queue的底层默认容器 三&#xff0c;queue实现 1. 普通queue …

gradle 命令行单元测试执行问题

文章目录 问题&#xff1a;命令行 执行失败最终解决方案&#xff08;1&#xff09;ADB命令&#xff08;2&#xff09;Java 环境配置 问题&#xff1a;命令行 执行失败 命令行 执行测试命令 无法使用&#xff08;之前还能用的。没有任何改动&#xff0c;又不能用了&#xff09; …

DC-7靶机

DC-7靶机地址 同样的&#xff0c;把靶机跟kali放在同一网段&#xff0c;&#xff08;NAT模式&#xff09; 主机发现 arp-scan -l端口扫描 nmap -A -T4 -p- 192.168.80.13922端口开始&#xff0c;80端口开启 浏览器先访问一下靶机的80端口 熟悉的Drupal站点 先爆破一下目录…

深入学习 Redis - 事务、实现原理、指令使用及场景

目录 一、Redis 事务 vs MySQL事务 二、Redis 事务的执行原理 2.1、执行原理 2.2、Redis 事务设计这么简单&#xff0c;为什么不涉及成 MySQL 那样强大呢&#xff1f; 三、Redis 事务的使用 3.1、使用场景 3.2、具体演示 开启/执行/放弃事务 watch 监控 watch 实现原理…

【Terraform学习】保护敏感变量(Terraform配置语言学习)

实验步骤 创建 EC2 IAM 角色 导航到IAM 在左侧菜单中&#xff0c;单击角色 。单击创建角色该按钮以创建新的 IAM 角色。 在创建角色部分&#xff0c;为角色选择可信实体类型&#xff1a; AWS 服务 使用案例:EC2 单击下一步 添加权限&#xff1a;现在&#xff0c;您可以看到…

HA3 SQL样本实验:一种混合计算查询的全新样本解决方案

作者&#xff1a;陆唯一(芜霜) HA3&#xff08;对外开源代号&#xff1a;Havenask &#xff09;是阿里智能引擎团队自研的大规模分布式检索系统&#xff0c;广泛应用于阿里内部的搜索业务&#xff0c;是十多年来阿里在电商领域积累下来的核心竞争力产品。Ha3 SQL 是在原有Ha3引…