C语言——操作符详解

目录

1.操作符的分类

2.原码、反码和补码

3.移位操作符

3.1 左移操作符

3.2 右移操作符

4.位操作符

4.1 按位与&

4.2 按位或|

4.3 按位异或^

​编辑 4.4 按位取反~

4.5 应用题 

4.5.1 题目:不能创建临时变量,实现两个整数的交换

4.5.2 题目:求一个整数存储在内存中的二进制中1的个数

4.5.3 题目:写一个代码,判断n是否为2的次方数 

4.5.4 二进制位置0或者置1

5.单目操作符 

6.逗号表达式

7.下标访问[ ]、函数调用( )

7.1 [ ] 下标引用操作符

7.2 函数调用操作符

8.结构成员访问操作符

8.1 结构体

8.1.1 结构体的声明

8.1.2 结构体的定义和初始化

9.操作符的属性:优先级和结合性

9.1 优先级

9.2 结合性

10.表达式求值

10.1 整型提升

10.2 算数转换

10.3 问题表达式解析

10.3.1 表达式1

10.3.2 表达式2

10.3.3 表达式3

10.3.4 表达式4

10.3.5 表达式5


1.操作符的分类

操作符可以分为很多类

2.原码、反码和补码

整数的二进制表示方法有三种:原码、反码和补码

有符号整数的三种表示方法均有符号位和数值位两部分,二进制序列中最高位的一位是被当做符号位,剩余的都是数值位;符号位都是用0表示“正”,用1来表示“负”。

正整数的三码均相同;

负整数的三种表示方法不同。

原码:直接翻译成二进制的就是原码

反码:符号位不变,其他位次依次取反

补码:反码加1就得到补码

补码得到原码可以用——取反,+1的操作

对于整数来说,数据存放内存中其实存放的是补码

3.移位操作符

移动的是存储在内存中二进制位(即补码

<<为左移操作符

>>为右移操作符

3.1 左移操作符

规则:左边遗弃,右边补0

例如:

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

运行结果为:

分析:根据规则,a不变,其二进制表示为1010,而b为a的基础上在最后加了一个0,则b为10100

3.2 右移操作符

运算分为两种:

1.逻辑右移:左边用0填充,右边丢弃

2.算数右移:左边用原该值的符号位填充,右边丢弃

右移到底采用哪一种方法,取决于编译器,通常是采用算数右移

4.位操作符

位操作符有四种:

1. &       按位与

2.|         按位或

3.^        按位异或

4.~       按位取反

注意:他们的操作数必须为整数

4.1 按位与&

运算规则:有0为0,全1为1

例如:

int main()
{int a = 6;//00000000000000000000000000000110   ->6的补码int b = -7;//10000000000000000000000000000111   ->-7的原码//11111111111111111111111111111000   ->-7的反码//11111111111111111111111111111001   ->-7的补码//00000000000000000000000000000110   ->6的补码//00000000000000000000000000000000   ->&之后,因此为0int c = a & b;printf("c = %d\n", c);return 0;
}

根据推算,我们可以得到c为0,则运行结果为:

则可以知道推算结果正确。

4.2 按位或|

运算规则:有1为1,全0为0

例如:

int main()
{int a = 6;//00000000000000000000000000000110   ->6的补码int b = -7;//10000000000000000000000000000111   ->-7的原码//11111111111111111111111111111000   ->-7的反码//11111111111111111111111111111001   ->-7的补码//00000000000000000000000000000110   ->6的补码//11111111111111111111111111111111   ->|之后//10000000000000000000000000000000//10000000000000000000000000000001   ->因此为-1int c = a | b;printf("c = %d\n", c);return 0;
}

运行结果为:

因此,我们可以知道,推算结果是正确的。 

4.3 按位异或^

运算规则:相同为0,相异为1

例如:

int main()
{int a = 6;//00000000000000000000000000000110   ->6的补码int b = -7;//10000000000000000000000000000111   ->-7的原码//11111111111111111111111111111000   ->-7的反码//11111111111111111111111111111001   ->-7的补码//00000000000000000000000000000110   ->6的补码//11111111111111111111111111111111   ->^之后//10000000000000000000000000000000//10000000000000000000000000000001   ->因此为-1int c = a ^ b;printf("c = %d\n", c);return 0;
}

运行结果为:

 4.4 按位取反~

与前三个不同的是,~操作符是单目操作符

例如:

int main()
{int a = 0;printf("%d\n", ~a);//00000000000000000000000000000000//11111111111111111111111111111111//10000000000000000000000000000000//10000000000000000000000000000001//所以最终结果为-1return 0;
}

运行结果为:

4.5 应用题 

4.5.1 题目:不能创建临时变量,实现两个整数的交换

方法一:

int main()
{int a = 3;int b = 5;//实现过程printf("交换前:a = %d, b = %d", a, b);a = a + b;b = a - b;a = a - b;printf("交换后:a = %d, b = %d", a, b);return 0;
}

运行结果为:

这样看似是正确的,但是,我们需要注意的是int 类型是有范围的,当a和b所代表的数都很大的时候,就可能会出现问题,所以,我们还要对代码进行改进。

方法二:

运用上述的操作符来解决

int main()
{int a = 3;int b = 5;//实现过程printf("交换前:a = %d, b = %d\n", a, b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交换后:a = %d, b = %d\n", a, b);return 0;
}

 运行结果为:

我们需要记住一个结论为:a = b ^ b ^ a,即b ^ b = 0 ,0 ^ a = a

4.5.2 题目:求一个整数存储在内存中的二进制中1的个数

方法一:结合原先分别列出一个数据的方法来进行求解

int count_bit_one(unsigned int n)
{int count = 0;while (n){if ((n % 2) == 1)count++;n /= 2;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

 需要注意的是:我们要注意负数的情况,因此在函数定义的时候,应该为unsigned int

方法二:运用操作符来进行解决

分析:当n & 1 == 1时, 我们可以得出32位二进位中最后一个为1

int count_bit_one(unsigned int n)
{int i = 0;int count = 0;for (i = 0; i < 32; i++){if ((n >> i) & 1 == 1)count++;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

运行结果为:

所以,我们学习了操作符的知识后,要学会把它们运用到常见的问题中

 方法三:

对第二种方法进行改进——

n = n & (n - 1)来进行循环,循环了多少次,那么n中就有几个1(此处省略证明过程,有兴趣的可以举例进行验证)

int count_bit_one(int n)
{int i = 0;int count = 0;while (n){n = n & (n - 1);count++;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

运行结果为:

4.5.3 题目:写一个代码,判断n是否为2的次方数 

分析:2的次方数中,只含有一个1

因为 n & (n - 1) 是去掉一个1,因此,当 n & (n - 1) == 0 时,此时满足题意。

该题省略代码过程。

4.5.4 二进制位置0或者置1

例题:编写代码将13二进制序列的第五位修改为1,然后再改回0

 

int main()
{int a = 13;//00000000000000000000000000001101//00000000000000000000000000010000  ->借助这个数来得到改为1的数//00000000000000000000000000011101  ->改为1int n = 5;a = a | (1 << (n - 1));printf("%d\n", a);//00000000000000000000000000011101//11111111111111111111111111101111  ->&之后就可以得到原来的数//00000000000000000000000000001101  ->改为0a = a & ~(a << (n - 1));printf("%d\n", a);return 0;
}

运行结果为:

5.单目操作符 

单目操作符常见的有:|   ++   --   &   *   +   -   sizeof   (类型)

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

其中单目操作符时&为取地址操作符,在指针中运用广泛。

6.逗号表达式

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

为了比较好理解我们给出一个例子:

int main()
{int a = 1;int b = 2;int c = (a > b, a = b + 10, a, b = a + 1);printf("%d\n", c);return 0;
}

算c的值的时候,首先执行a > b 的语句,这里没有任何值的改变,接着执行 a =  b + 10,此时a的值发生改变,a的值变为12,然后执行a的语句,最后执行b = a + 1 的语句,此时b = 13,因此,最终c的值为13,我们运行一下来进行检验——

7.下标访问[ ]、函数调用( )

7.1 [ ] 下标引用操作符

操作数 :一个数组名 + 一个索引值(下标)

int arr[10];//创建数组
arr[9] = 10;//使用下标引用操作符
[ ]的两个操作数是arr和9

7.2 函数调用操作符

例如:

int main()
{printf("hello\n");//()就是函数调用操作符printf("%d \n", 1000);return 0;
}

操作数就是函数名和()中的内容,那么我们可以得出,函数调用操作符最少有一个操作数(函数名)。

8.结构成员访问操作符

8.1 结构体

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:标量、数组、指针,甚至是其他结构体。

8.1.1 结构体的声明

struct 名字
{结构体内容
};

例如:描述一个学生可以为

struct student
{char name[20];//名字int age;//年龄double score;//成绩
};

8.1.2 结构体的定义和初始化

通过以下的代码示例,我们来了解一下结构体的定义和初始化

struct student
{char name[20];//名字int age;//年龄double score;//成绩
}s4, s5, s6;//第一种定义方式struct student s3;//第二种定义方式int main()
{int a;struct student s1 = { "zhangsan", 20, 98.50 };//第三种定义方式struct student s2 = { "lisi", 35, 91.98 };//初始化方式return 0;
}

其中,第一种和第二种方法定义的变量为全局变量。

结构体方面的知识点在后续会有专门的补充,今天我们先简单了解一下~

9.操作符的属性:优先级和结合性

这两个属性决定了表达式求值的计算顺序。

9.1 优先级

各种运算符的优先级是不一样的,相邻操作符中,优先级高的先进行计算。

例如:

int main()
{int r = 3 + 4 * 5;printf("%d\n", r);return 0;
}

在上述的操作符中‘*’的优先级大于‘+’,所以应该先计算4 * 5 这一部分。

关于优先级,我们可以在网上搜索相应的规则和知识,这里就不再赘述。

9.2 结合性

相邻的操作符的优先级相同的情况下,由结合性来决定。

10.表达式求值

10.1 整型提升

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

例如:

int main()
{char a = 20;char b = 130;char c = a + b;printf("%d\n", c);return 0;
}

此时,就把a和b转换为了普通整型来进行运算。

意义CPU内的整型运算器(ALU)的操作数的字节长度和int相同,也是CPU的通用寄存器长度。表达式中各种长度可能小于Int长度的整型值,都必须先转换为unsigned int 或者Int,才能送入CPU进行运算。

如何进行整型提升呢

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

2.无符号整数提升,高位补0

10.2 算数转换

算数转换讨论的就是类型大于等于整型类型的类型,如long double、double、float、long int、int等类型的数据。

下面的层次为寻常算数转换:

1    long double
2    double
3    float
4    unsigned long int 
5    long int 
6    unsigned int
7    int

为从下到上进行转换。

10.3 问题表达式解析

10.3.1 表达式1

a * b + c * d + e * f

在这个表达式中,只能确定 * 的计算比 + 的早,但是优先级并不能决定第三个 * 比第一个 + 早执行。

10.3.2 表达式2

c + --c;

这时,前一个c中存的数不确定是原来的,还是--之后的。

10.3.3 表达式3

表达式过于复杂,影响可读性。

例如:

i = i-- - --i * (i = -3) * i++ + ++i

经过运行,我们可以发现在不同的编译器中,所得到的结果不同,这样代码本身就存在问题。

10.3.4 表达式4

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

这时,存在的问题是,我们不知道fun( )所调用出来的数赋值在哪里。

10.3.5 表达式5

int main()
{int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0;
}

这时代码也存在问题,我们不知道 ret i 进行了几次自增。

在不同的编译器中,所得到的结果也是不一样的。所以,这个代码是存在问题的。

所以——即使我们知道了操作符的优先级和结合性,我们写出的代码也是存在风险的,因此,我们最好不要写过于复杂的表达式,既影响了可读性,也容易出错。

今天就到这里,我们下个知识点见~

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

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

相关文章

本地下载安装WampServer结合内网穿透配置公网地址远程访问详细教程

文章目录 前言1.WampServer下载安装2.WampServer启动3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 Wamp 是一个 Windows系统下的 Apache PHP Mysql 集成安装环境&#xff0c;是一组常用来…

FFMPEG推流器讲解

FFMPEG重要结构体的讲解 FFMPEG中有六个比较重要的结构体&#xff0c;分别是AVFormatContext、AVOutputFormat、 AVStream、AVCodec、AVCodecContext、AVPacket、AVFrame、AVIOContext结构体&#xff0c;这几个结构体是贯穿着整个FFMPEG核心功能。 AVFormatContext 这个结构…

nginx基础配置

1. https配置 首先在nginx.conf中配置https 2. 重定向 rewrite ^/(.*)$ https://www.sxl1.com/$1 permanent;3. 自动索引 autoindex on;4. 缓存 Nginx expire缓存配置: 缓存可以降低网站带宽&#xff0c;加速用户访问location ~ .*\.(gif|jpg|png)$ {expires 365d;roo…

react最好用的swiper插件和拖动插件 react-tiny-slider react-draggable

react移动端项目&#xff0c;其实有挺多的ui框架的&#xff0c;但是我们公司的项目&#xff0c;都是自己封装的ui库&#xff0c;又不可能为了一个轮播图就去再安装一个ui库 所以找了很多的轮播插件&#xff0c;都是不能满足需求 最后找到了它&#xff0c;react-tiny-slider&…

Windows Microsoft Edge 浏览器 配置【密码】

在浏览 Web 时&#xff0c;Microsoft Edge 可以轻松保存密码。 在桌面或移动设备上的 Edge 浏览器中输入新密码时&#xff0c;Microsoft Edge 会询问你是否要记住用户名和密码。 下次访问该网站时&#xff0c;浏览器将完成帐户信息的填写。 如果使用 Microsoft 帐户登录到 Edg…

redis集合若干记录

无序集合 redis通常使用字典结构保存集合数据&#xff0c;字典健存储集合元素&#xff0c;字典值为空。如果一个集合全为整数&#xff0c;使用字典就有点浪费了&#xff0c;redis使用intset保存。 插入元素到intset中 获取插入元素编码&#xff0c;如果插入元素编码级别高于int…

机器学习 之 sklearn的使用介绍和如何找到API

scikit-learn&#xff08;简称 sklearn&#xff09;是基于python语言的一个第三方机器学习库&#xff0c;它提供了简单而有效的工具来进行数据分析和建模。建立在numpy pandas SciPy和Malpotlib库上&#xff0c;下面是对如何使用 sklearn 以及如何找到其 API 的一个基本介绍&am…

仿RabbiteMq实现简易消息队列正式篇(需求分析)

TOC 目录 MQ的实现方法 RabbitMq中的相关概念 消息队列系统模块划分 总体划分 服务端模块 数据管理模块 虚拟机数据管理模块 交换机路由模块 消费者管理模块 信道&#xff08;通信&#xff09;管理模块 连接管理模块 服务端BrokerServer模块 客户端模块 消费者管…

MySQL-MVCC举例说明

在数据库系统中&#xff0c;多版本并发控制&#xff08;MVCC, Multi-Version Concurrency Control&#xff09; 是一种用于提高并发性能的机制&#xff0c;它允许多个事务同时读取和写入数据&#xff0c;而不会产生锁等待和阻塞的问题。MySQL 的 InnoDB 存储引擎广泛使用了 MVC…

Keepalived 高可用集群详解和配置

Keepalived 高可用集群 集群类型 1、LB&#xff08;Load Balance&#xff09;&#xff1a;负载均衡 LVS&#xff1a;四层负载均衡 HAProxy&#xff1a;七层/四层 负载均衡 nginx&#xff1a;七层负载均衡 (http/upstream,stream/upstream) 2、HA&#xff08;High Availa bili…

C++ 设计模式——工厂方法模式

工厂方法模式 工厂方法模式主要组成部分代码实现工厂方法模式模式的 UML 图工厂方法模式 UML 图解析优点和缺点适用场景 工厂方法模式 工厂方法模式是一种创建型设计模式&#xff0c;它通过定义一个接口用于创建对象&#xff0c;但由子类决定实例化哪个类。与简单工厂模式不同…

Spring项目:文字花园(三)

一.实现博客详情 1.后端逻辑代码 controller层添加方法&#xff08;根据博客id查看博客详情&#xff09; RequestMapping("/getBlogDetail")public Result<BlogInfo> getBlogDetail(Integer blogId){log.info("getBlogDetail, blogId: {}", blogId…

vue 后台管理 之 状态管理 vuex 的使用

幸福是一种能力 文章目录 一、数据驱动视图二、VueX 数据公共池 一、数据驱动视图 我们都知道 vue 之所以好用&#xff0c;是因为官方帮我们做了数据驱动视图初始化时将数据和视图进行绑定&#xff0c;通过 watcher 来监听数据的变化&#xff0c;当数据变化时&#xff0c;会触…

这三大创意神器,一个不用就是错过了一个小目标!

【导语】在这个充满无限可能的数字时代&#xff0c;创意成为了推动我们前行的强大动力。但你是否曾因视频背景杂乱无章而苦恼&#xff1f;是否渴望将静态图片瞬间赋予生命&#xff1f;又或是对模糊照片中的珍贵瞬间束手无策&#xff1f;今天&#xff0c;就让我们揭秘三大变态且…

Linux搭建环境:从零开始掌握基础操作(二)

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 软件测试第一步就是搭建测试环境&#xff0c;如何搭建好测试环境&#xff0c;需要具备两项的基础知识&#xff1a; 1、Linux 命令: 软件测试第一个任务, 一般都需要进行环境搭建, 一部分&#xff0c;环境搭建内容是在服…

Your local changes would be overwritten by merge git

方法二 直接覆盖本地的代码&#xff0c;放弃自己本地的改动&#xff0c;只保留服务器端代码 直接回退到上一个版本&#xff0c;再进行pull。 【步骤】 直接 VCS -> Git -> Reset HEAD… 选择需要的reset模式&#xff1a;hard&#xff08;即放弃本地代码&#xff0c;新修…

JavaScript基础——闭包

闭包简介 闭包的作用 闭包可以保留变量的状态 闭包可以让变量私有化 闭包的缺点 闭包简介 在JavaScript中&#xff0c;重复声明同一个变量会导致变量冲突&#xff0c;在这个时候可以使用闭包创建独立的执行环境。 在JavaScript中&#xff0c;闭包是指封闭的执行环境&#xff…

【图像去噪】论文精读:Toward Convolutional Blind Denoising of Real Photographs(CBDNet)

文章目录 前言Abstract1. Introduction2. Related Work2.1. Deep CNN Denoisers2.2. Image Noise Modeling2.3. Blind Denoising of Real Images 3. Proposed Method3.1. Realistic Noise Model3.2. Network Architecture3.3. Asymmetric Loss and Model Objective3.4. Trainin…

Spring IoCDI(下)—DI的尾声

我们之前学习了控制反转IoC&#xff0c;接下来就开始学习依赖注入DI的细节。 依赖注入是一个过程&#xff0c;是指IoC容器在创建Bean时&#xff0c;去提供运行时所依赖的资源&#xff0c;而资源指的就是对象。我们使用 Autowired 注解&#xff0c;完成依赖注入的操作。简单来说…

使用docker compose一键部署redis服务

使用docker compose一键部署redis服务 1、创建安装目录 mkdir /data/redis/ -p && cd /data/redis2、创建docker-compose.yml文件 version: 3 services:redis:image: registry.cn-hangzhou.aliyuncs.com/xiaopangpang/redis:7.0.5container_name: redisrestart: al…