当前位置: 首页 > ai >正文

C 语言部分操作符详解 -- 进制转换,原码、反码、补码,位操作符,逗号表达式,操作符的优先级和结合性,整型提升,算术转换

目录

1. 操作符分类

2. 二进制和进制转换

2.1 2 进制和 10 进制的相互转换

2.2 2 进制和 8 进制、16 进制的转换

3. 原码、反码、补码

4. 位操作符

5. 逗号表达式

6. 下表访问[]、函数调用()

8. 操作符的属性:优先级、结合性

9. 表达式求值

9.1 整型提升

9.2 算术转换


1. 操作符分类

        C 语言中很很多操作符,根据功能可以将操作符分为很多类,比如算数操作符、位操作符、赋值操作符、关系操作符、逻辑操作符等等。关系操作符、逻辑操作符以及条件操作符在https://blog.csdn.net/CSQCSQCC/article/details/137715007 中已经有所介绍,算数操作符以及部分单目操作符在https://blog.csdn.net/CSQCSQCC/article/details/148639715中已经有所介绍。

2. 二进制和进制转换

        2 进制、8 进制、10 进制、16 进制是数值的不同表示形式

15的2进制:1111
15的8进制:17
15的10进制:15
15的16进制:F

        10 进制就是满 10 进 1,每一位都是 0-9 的数组组成。相对的二进制就是满 2 进 1,每一位都是 0-1 的数字组成。 

2.1 2 进制和 10 进制的相互转换

        10 进制的每一位是权重,10 进制的数字从右往左是个位、十位、百位...,分别每一位的权重是 10 的 0 次方,10 的 1 次方,10 的 2 次方...

        2 进制和 10 进制类似,只不过 2 进制的每一位权重不一样,从右往左分别是 2 的 0 次方,2 的 1 次方,2 的 2 次方... 

        现在将 10 进制的 125 转为 2进制,则将 125 依次除以 2,每次除以 2 的余数就是 2 进制中从低数值位到高数值位的值。

        如上图,最后得到的二进制就是从下到上的余数。 

        2 进制转成 10 进制,就是将 2 进制每一位的值乘以每一位的权重然后相加即可

2.2 2 进制和 8 进制、16 进制的转换

        8 进制的数字每一位是 0-7 的数字组成,写成 2 进制就是 3 位 2 进制数字,比如 8 进制的 7 就是 2 进制的 111,所以在 2 进制转 8 进制的时候,从低位开始,每 3 位转换成一个 8 进制数字即可,最后一组不够 3 位则将高位看做 0 即可

        如上图,01 转成 8 进制的 1,101 转为 8 进制的 5,011 转为 8 进制的 3,所以 2 进制的 01101011 就会转成 8 进制的 153。 

        16 进制的数字每一位是 0-9 以及 a-f 的数字组成,和 8 进制相似,16 进制写成 2 进制就是 4 位 2 进制数字,所以在 2 进制转 16 进制的时候,从低位开始,每 4 位转换成一个 16 进制数字即可,最后一组不够 4 位则将高位看做 0 即可

        如上图,0110 转成 16 进制的 6,1011 转为 16 进制的 b,所以 2 进制的 01101011 就会转成 16 进制的 6b。 

3. 原码、反码、补码

        整数的 2 进制表示方法有三种,原码、反码、补码。

        有符号整数的三种表示方法均有符号位数值位,2 进制序列中,最高位的 1 位是被当做符号位,0 表示“正”,1 表示“负”,剩余的都是数值位

        (1)正整数的原码、反码、补码都相同。

        (2)负整数的原码、反码、补码各不相同。

                原码:将数值按照正负数的形式翻译成 2 进制得到的就是原码。

                反码:原码的符号位不变,其他位依次按位取反得到的就是反码。

                补码:反码 + 1 就得到的是补码。反码取反然后 + 1 得到的就是原码。

        在计算机系统中,数值一律用补码来表示和存储。因为使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理,此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

4. 位操作符

操作符名称描述
&按位与对两个操作数逐位进行与运算
|按位或对两个操作数逐位进行或运算
^按位异或对两个操作数逐位进行异或运算
~按位取反对两个操作数逐位进行取反运算
<<左移将操作数的 bit 位左移指定位数
>>右移将操作数的 bit 位右移指定位数

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

#include <stdio.h>
int main()
{int num1 = -3;// -3 存在 int a 中,是 4 个字节,32 个 bit 位// 源码:1000 0000 0000 0000 0000 0000 0000 0011// 反码:1111 1111 1111 1111 1111 1111 1111 1100// 补码:1111 1111 1111 1111 1111 1111 1111 1101int num2 = 5;// 5 存在 int a 中,是 4 个字节,32 个 bit 位,整数原码,反码,补码都一样// 源码:0000 0000 0000 0000 0000 0000 0000 0101// 反码:0000 0000 0000 0000 0000 0000 0000 0101// 补码:0000 0000 0000 0000 0000 0000 0000 0101// -3 补码:1111 1111 1111 1111 1111 1111 1111 1101// 5  补码:0000 0000 0000 0000 0000 0000 0000 0101printf("%d\n", num1 & num2);//	  补码:0000 0000 0000 0000 0000 0000 0000 0101//	  反码:0000 0000 0000 0000 0000 0000 0000 0101//	  源码:0000 0000 0000 0000 0000 0000 0000 0101 -- 5printf("%d\n", num1 | num2);//	  补码:1111 1111 1111 1111 1111 1111 1111 1101//	  反码:1000 0000 0000 0000 0000 0000 0000 0010//	  原码:1000 0000 0000 0000 0000 0000 0000 0011 -- -3printf("%d\n", num1 ^ num2);//	  补码:1111 1111 1111 1111 1111 1111 1111 1000//	  反码:1000 0000 0000 0000 0000 0000 0000 0111//	  原码:1000 0000 0000 0000 0000 0000 0000 1000 -- -8printf("%d\n", ~0);// 0 补码:	  0000 0000 0000 0000 0000 0000 0000 0000// 补码取反:  1111 1111 1111 1111 1111 1111 1111 1111 -- 补码//            1000 0000 0000 0000 0000 0000 0000 0000 -- 反码//            1000 0000 0000 0000 0000 0000 0000 0001 -- 原码 -- -1return 0;
}

        例:不创建临时变量,实现两个整数的交换。 

// 方法1:
#include <stdio.h>
int main()
{int a = 3;int b = 5;printf("交换前: a = %d, b = %d", a, b);a = a + b;	// a 为 a 和 b 的和b = a - b;	// b = a 和 b 的和减去 a,则 b = aa = a - b;	// 上一步中,b 已经等于 a 了,再用 a 和 b 的和减去 a 得到 bprintf("交换后: a = %d, b = %d", a, b);return 0;
}// 该方法在 a + b 超过整型的最大值时,就会出现溢出的情况,计算出来的值不对
// 方法2:异或
#include <stdio.h>
int main()
{int a = 3;int b = 5;printf("交换前: a = %d, b = %d", a, b);a = a ^ b;b = a ^ b;	// b = a ^ b ^ b = a;a = a ^ b;	// a = a ^ b ^ a = b;printf("交换后: a = %d, b = %d", a, b);return 0;
}// a ^ a = 0; a ^ 0 = a;

        左移操作符 " << ":左边抛弃,右边补0。 

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

        右移操作符 " >> ":(1)逻辑右移:左边用 0 填充,右边丢弃。(2)算数右移:左边用原该值的符号位填充,右边丢弃。 

        右移到底是算数右移还是逻辑右移是取决于编译器的实现,常见的编译器都是算数右移。

        算数右移:

        逻辑右移:

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

5. 逗号表达式

        逗号表达式就是用逗号隔开多个表达式逗号表达式从左往右依次执行,整个表达式的结果是最后一个表达式的结果

#include <stdio.h>int main()
{int a = 1;int b = 2;int c = (a > b, a = b + 10, a, b = a + 1);	//逗号表达式printf("c=%d", c);return 0;
}

        在上述逗号表达式中,从左向右计算,a 的值被更新为 12,b 的值被更新为 13,则 c 等于最后一个表达式的结果,所以 c = b = 13。 

        逗号表达式在 while 循环中使用可以减少代码冗余。 

a = get_val();
count_val(a);
while (a > 0)
{//业务处理//...a = get_val();count_val(a);
}// 如果使⽤逗号表达式,改写为如下代码,可以减少代码冗余
while (a = get_val(), count_val(a), a>0)
{//业务处理
}

6. 下表访问[]、函数调用()

        (1)下标引用操作符 []:该操作符的操作数为一个数组名和一个索引值

int arr[10];    // 创建数组。
arr[9] = 10;    // 使用下标引⽤操作符。

        (2)函数调用操作符 ():接收一个或者多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数

#include <stdio.h>
void test1()
{printf("hehe\n");
}
void test2(const char* str)
{printf("%s\n", str);
}
int main()
{test1();	// ()作为函数调⽤操作符。test2("hello XiaoC.");	// ()就是函数调⽤操作符。return 0;
}

8. 操作符的属性:优先级、结合性

        C 语言的操作符有两个重要的属性:优先级和结合性,这两个属性决定了表达式求值的计算顺序。

        (1)优先级:如果一个表达式包含多个运算符,运算符的优先级决定哪个运算符应该优先执行。

3 + 4 * 5;

        上述表达式中既有加法运算也有乘法运算,由于乘法运算优先级高于加法,所以先计算乘法。 

        (2)结合性:如果两个运算符优先级相同,优先级没办法决定哪个先计算,根据运算符左结合还是右结合,决定执行顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。

5 * 6 / 2;

        上述乘法和除法的优先级相同,都是左结合运算符,所以从左到右执行。 

9. 表达式求值

9.1 整型提升

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

        整型提升规则:(1)有符号整数提升是按照变量的数据类型的符号位来提升的。(2)无符号整型提升,高位补0

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

        这里解释为什么上述结果为 126。

#include <stdio.h>int main()
{char a = 3;// 00000011 -- a在内存中存储的补码char b = 127;// 01111111 -- b在内存中存储的补码char c = a + b;// 当a和b进行运算时要进行整型提升// 00000000 00000000 00000000 00000011 -- a整型提升后在内存中存储的补码// 00000000 00000000 00000000 01111111 -- b整型提升后在内存中存储的补码// 00000000 00000000 00000000 10000010 -- a+b运算的结果在内存中存储的补码// 10000010 -- c在内存中存储的补码printf("c = %d", c);// 使用%d进行打印的时候也是需要进行整型提升的,c为有符号字符,所以整型提升时按照符号为进行提升// 11111111 11111111 11111111 10000010 -- c整型提升后的补码// 10000000 00000000 00000000 01111101 -- c整型提升后的反码// 10000000 00000000 00000000 01111110 -- c整型提升后的原码 -- -126return 0;
}

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

9.2 算术转换

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

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

        在上述表中,如果一个操作数的类型排在另外一个操作数类型的后面,则该操作数要先转换为另外一个操作数的类型再进行计算。比如 int 类型和 double 类型进行计算,则 int 类型要先转换成 double 类型在进行计算。 

http://www.xdnf.cn/news/15383.html

相关文章:

  • 2025年小目标检测分享:从无人机视角到微观缺陷的创新模型
  • 【PTA数据结构 | C语言版】二叉树前序序列化
  • Python初学者笔记第十二期 -- (集合与字典编程练习题)
  • Vim多列操作指南
  • TCP可靠性设计的核心机制与底层逻辑
  • next.js 登录认证:使用 github 账号授权登录。
  • uni-app+vue3 来说一说前端遇到跨域的解决方案
  • 全连接神经网络
  • 10分钟搞定!Chatbox+本地知识库=你的私人语音导师:企业级全栈实现指南
  • 自动微分模块
  • JAR 包冲突排雷指南:原理、现象与 Maven 一站式解决
  • 机载激光雷达目标识别:从点云到凝视成像的算法全景
  • Datawhale AI夏令营——用户新增预测挑战赛
  • xss-lab靶场通关
  • 苦练Python第18天:Python异常处理锦囊
  • 从 JSON 到 Python 对象:一次通透的序列化与反序列化之旅
  • 云原生技术与应用-Containerd容器技术详解
  • Android系统的问题分析笔记 - Android上的调试方式 bugreport
  • RAG索引流程中的文档解析:工业级实践方案与最佳实践
  • iOS —— 网易云仿写
  • 大数据系列之:通过trino查询hive表
  • 直播推流技术底层逻辑详解与私有化实现方案-以rmtp rtc hls为例-优雅草卓伊凡
  • 在Linux下git的使用
  • 量子计算新突破!阿里“太章3.0”实现512量子比特模拟(2025中国量子算力巅峰)
  • MYOJ_8512:CSP初赛题单1:计算机常识
  • 计算机网络通信的相关知识总结
  • Linux进程优先级机制深度解析:从Nice值到实时调度
  • 图机器学习(1)——图论基础
  • Django Admin 配置详解
  • 【C语言进阶】指针面试题详解(2)