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 进行了几次自增。
在不同的编译器中,所得到的结果也是不一样的。所以,这个代码是存在问题的。
所以——即使我们知道了操作符的优先级和结合性,我们写出的代码也是存在风险的,因此,我们最好不要写过于复杂的表达式,既影响了可读性,也容易出错。
今天就到这里,我们下个知识点见~
相关文章:

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 …...
【Linux】内核全量函数添加日志打印摸索
1、操作系统在空载时要把函数调用次数非常多的注释掉,这里打印时不能带进程名称,高执行概率函数不同进程执行到的概率也很高,不然操作业务会增加卡死的概率; 2、卡死一般是调用次数太多导致,会卡住操作系统十多秒&…...
24/8/17算法笔记 CQL算法离线学习
离线学习:不需要更新数据 CQL(Conservative Q-Learning)算法是一种用于离线强化学习的方法,它通过学习一个保守的Q函数来解决标准离线RL方法可能由于数据集和学习到的策略之间的分布偏移而导致的过高估计问题 。CQL算法的核心思想…...

C++第十一弹 -- STL之List的剖析与使用
文章索引 前言1. list的介绍2 list的使用2.1 list的构造函数2.2 iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers 3. list的迭代器失效4. list与vector的对比总结 前言 本篇我们旨在探讨对于STL中list的使用, 下一篇我们将会对list进行底层剖析以及…...

物流快递外卖管理平台系统-计算机毕设Java|springboot实战项目
🍊作者:计算机毕设匠心工作室 🍊简介:毕业后就一直专业从事计算机软件程序开发,至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长:按照需求定制化开发项目…...
开源BaaS 平台介绍
以下是几款常见的开源后端平台,它们提供了用户管理、权限验证、文件存储、API 管理等类似的后端功能。 1. Parse Server 简介: Parse 是一个非常流行的开源后端服务平台,它最初由 Facebook 开发,后来开源。它支持用户管理、数据存储、文件存…...

分享一个基于python爬虫的“今日头条”新闻数据分析可视化系统(源码、调试、LW、开题、PPT)
💕💕作者:计算机源码社 💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流&…...
QT自定义信号槽
1.自定义信号槽 使用connect()可以让我们连接系统提供的信号和槽,同时也可以自定义信号槽。 例如以学生和老师构建类同时当老师触发信号下课同学收到信号执行“吃饭”这一动作代码示例 #include "SignalAndSlot.h" //Teacher Student 总框架…...

one-shot 序列图像红外小目标分割
one-shot 序列图像红外小目标分割 IEEE TRANSACTIONS ON GEOSCIENCE AND REMOTE SENSING 代码还未开源 GitHub - D-IceIce/one-shot-IRSTS few-shot:利用少量标注样本进行学习 one-shot: 属于few-shot的特殊情况,只用一个样本进行学习 zero-shot&am…...
JavaScript 单线程防阻塞的原理
JavaScript 是一种单线程语言,这意味着它一次只能执行一个任务。这种设计可能会导致一些问题,比如当遇到耗时的操作时,整个程序可能会被阻塞。为了解决这个问题,JavaScript 使用了事件循环和回调函数的机制,实现了非阻塞式的异步操作。 事件循环 JavaScript 有一个事件队列,用…...

Shell脚本发送邮件的详细步骤与配置方法?
Shell脚本发送邮件的进阶技巧?怎么配置Shell脚本发信? 使用Shell脚本发送邮件是一种高效的自动化手段,特别是在需要定期发送报告、通知或警告信息时。AokSend将详细介绍Shell脚本发送邮件的步骤与配置方法,帮助您更好地掌握这一技…...

如何把Phalcon 集成到PhpStorm里面
一 背景 按照上一篇文章里面写的Phalcon 创建项目过程中的一些坑, 最终我们在终端可以基于Phalcon命令创建对应的开发项目。但在这个过程中,存在一个问题:那就是写代码的时候,发现Phalcon对应的依赖提示都没有,如下: 从上面这个截图来看,就能发现,Phalcon的啥…...

python从入门到精通:循环语句
目录 前言 1、while循环的基础语法 2、while循环的嵌套 3、for循环的基础语法 range语句: for循环临时变量作用域: 4、for循环的嵌套 5、循环中断:break和continue 前言 循环普遍存在于日常生活中,同样,在程序中…...

Codeforces Round 965 (Div. 2)
前言 有人在过七夕,我在打 cf ,还有某人独自一人在学校机房,凌晨一点骑上共享单车回宿舍欣赏沿途的秋风扫落叶。 Standings:2166 题目链接:Dashboard - Codeforces Round 965 (Div. 2) - Codeforces A. Find K Distin…...

Win10下载安装Mysql服务
Win10下载安装MySQL 一、官网下载MySQL 1.官网地址: https://www.mysql.com/ 2.在官网首页拉到最下方,点击MySQL Community Server: 3.根据个人电脑的操作系统选择,此处以Windows x64为例,选择第2个,点击…...
MVVM(Model-View-ViewModel)架构模式
在Android开发中,MVVM(Model-View-ViewModel)架构模式已经成为构建可维护和可扩展应用程序的重要选择。MVVM模式通过分离视图(View)、模型(Model)和视图模型(ViewModel)来…...

C#MVC返回DataTable到前端展示。
很久没写博客了,闭关太久,失踪人口回归,给诸位道友整点绝活。 交代下背景:要做一个行转列的汇总统计,而且,由于是行转列,列的数量不固定,所以,没法使用正常的SqlSugar框…...
HttpUtils工具类(二)Apache HttpClient 5 使用详细教程
目录 一、Apache HttpClient 5介绍 (1)核心特性 (2)Apache HttpClient 5 的新特性 (3)在 Java 项目的主要使用场景及缺点 使用场景: 缺点: 二、在实际项目中的应用 …...

Vue3.0生命周期钩子(包含:Vue 2.0 和 Vue 3.0)
1、Vue 2.0 生命周期钩子 每个应用程序实例在创建时都有一系列的初始化步骤。例如,创建数据绑定、编译模板、将实例挂载到 DOM 并在数据变化时触发 DOM 更新、销毁实例等。在这个过程中会运行一些叫做生命周期钩子的函数,通过这些钩子函数可以定义业务逻…...

遥感之常用各种指数总结大全
目前在遥感领域基本各种研究领域都会用到各种各样的指数,如水体指数,植被指数,农业长势指数,盐分指数,云指数,阴影指数,建筑物指数,水质指数,干旱指数等等众多。 本文对上…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...