首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >收集飞花令碎片——C语言操作符与进制转换

收集飞花令碎片——C语言操作符与进制转换

作者头像
枫亭湖区
发布2025-11-13 09:06:25
发布2025-11-13 09:06:25
20
举报

C语言中的操作符用于执行各种运算和操作,主要包括算术操作符、关系操作符、逻辑操作符、位操作符、赋值操作符等。

1.进制转换

在计算机编程的学习中,其实我们经常能听到2进制8进制10进制16进制 这样的讲法,那是什么意思呢? 其实2进制8进制10进制16进制是数值的不同表示形式而已。

比如:数值15的各种进制的表示方式

  • 15的2进制:1111
  • 15的8进制:17
  • 15的10进制:15
  • 15的16进制:F
  • //16进制的数值之前写:0x
  • //8进制的数值之前写:0

(1)二进制

我们先重点讲解一下二进制

十进制图表

请添加图片描述
请添加图片描述

二进制图表

请添加图片描述
请添加图片描述

(2)八进制

八进制的每一位数字都是由0~7组成,正所谓满8进1

八进制位数与权重表

位数

权重(8的幂)

示例值

计算方式

第1位

8^0 = 1

1

1 * 8^0

第2位

8^1 = 8

2

2 * 8^1

第3位

8^2 = 64

3

3 * 8^2

第4位

8^3 = 512

4

4 * 8^3

第5位

8^4 = 4096

5

5 * 8^4

第6位

8^5 = 32768

6

6 * 8^5

第7位

8^6 = 262144

7

7 * 8^6

第8位

8^7 = 2097152

0

0 * 8^7

(3)16进制

十六进制(Hexadecimal)是一种基数为16的计数系统,使用0-9的数字和A-F的字母来表示数值。

16进制权重计算表

16进制位

权重(16^n)

示例值

计算方式

0

16^0 = 1

0xF

15 * 1

1

16^1 = 16

0xE

14 * 16

2

16^2 = 256

0xD

13 * 256

3

16^3 = 4096

0xC

12 * 4096

4

16^4 = 65536

0xB

11 * 65536

(4)进制转换

10进制转换成2进制
请添加图片描述
请添加图片描述
2进制转换成8进制
请添加图片描述
请添加图片描述
二进制转换成十六进制
请添加图片描述
请添加图片描述

2.操作符的分类

  • 算术操作符: + 、- 、* 、/ 、%
  • 移位操作符: << >>
  • 位操作符: & | ^
  • 赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
  • 单⽬操作符: !、++、–、&、*、+、-、~ 、sizeof、(类型)
  • 关系操作符: > 、>= 、< 、<= 、 == 、 !=
  • 逻辑操作符: && 、||
  • 条件操作符: ? :
  • 逗号表达式: ,
  • 下标引⽤: []
  • 函数调⽤: ()

3.原码、反码、补码(了解即可)

整数的2进制表示方法有3种,即原码反码补码 有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最⾼位的1位是被当做符号位,剩余的都是数值位。

符号位都是⽤0表示“正”,用1表示“负”。

请添加图片描述
请添加图片描述

正整数的原、反、补码都相同 负整数的三种表达方式各不相同

原码: 直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。 反码: 将原码的符号位不变,其他位依次按位取反就可以得到反码。 补码: 反码+1就得到补码。 补码得到原码也是可以使⽤:取反,+1的操作。

在计算机中,整数通常以补码存储,但理解补码需要先了解原码反码

4.位操作符

C语言中的位操作符用于直接操作整数的二进制位。以下是C语言中常用的位操作符及其功能: 移位操作符的操作数只能是整数,并且是转换成二进制去运行的

4.1移位操作符

<<左移操作符

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

代码语言:javascript
复制
#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. 算术右移:左边用原来该值的符号位填充,右边丢弃
代码语言:javascript
复制
#include <stdio.h>
int main(){
	int num = 10;
	int n = num >> 1;
	printf("n = %d\n",n);
	printf("num = %d\n",num);
	return 0;
}
请添加图片描述
请添加图片描述
注意

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

4.2按位操作符

& 按位与 | 按位或 ^ 按位异或 ~ 按位取反 它们的操作数必须是整数

按位与&

两个位都为1时结果为1,否则为0;任何数跟 1 与,结果还是它自己;跟 0 与,结果一定是 0

代码语言:javascript
复制
unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a & b; // 0001 (1)
按位或|

两个位中至少有一个为1时结果为1,否则为0

代码语言:javascript
复制
unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a | b; // 0111 (7)
按位异或 ^

两个位不同时结果为1,相同时为0

代码语言:javascript
复制
unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a ^ b; // 0110 (6)
按位取反~

0变1,1变0

代码语言:javascript
复制
unsigned char a = 5;    // 00000101
unsigned char b = ~a;   // 11111010 (250)

4.3复合赋值位操作符

C语言还提供了位操作符与赋值操作符结合的复合运算符:

  • 按位与赋值 &=
代码语言:javascript
复制
a &= b;  // 等价于 a = a & b;
  • 按位或赋值 |=
代码语言:javascript
复制
a |= b;  // 等价于 a = a | b;
  • 按位异或赋值 ^=
代码语言:javascript
复制
a ^= b;  // 等价于 a = a ^ b;
  • 左移赋值 <<=
代码语言:javascript
复制
a <<= n; // 等价于 a = a << n;
  • 右移赋值 >>=
代码语言:javascript
复制
a >>= n; // 等价于 a = a >> n;

4.4位运算符使用技巧与练习

  • 技巧 不能创建第三个变量,从而实现两个临时变量的交换
代码语言:javascript
复制
#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
		a = a ^ b;
		b = a ^ b;
		a = a ^ b;
			printf("a = %d b = %d\n", a, b);
		return 0;
}

5.单目操作符

单目操作符(Unary Operators)是只需要一个操作数的操作符。

5.1算术类单目操作符

  • 正号 + 功能: 保持操作数的正值(通常无实际效果)
  • 负号 - 功能: 对操作数取负值(算术取反)
  • 递增 ++
  1. 前置递增 ++var 功能: 先增加操作数的值,然后返回增加后的值
  2. 后置递增 var++ 功能: 先返回操作数的值,然后增加操作数的值
  • 递减 - -
  1. 前置递减 --var 功能: 先减少操作数的值,然后返回减少后的值
  2. 后置递减 var- - 功能: 先返回操作数的值,然后减少操作数的值

5.2逻辑与位操作类单目操作符

  • 逻辑非 ! 功能: 对操作数进行逻辑取反

操作数类型:标量类型(非零值变为0,零值变为1)

  • 按位取反 ~ 功能: 对操作数的每一位进行按位取反

操作数类型:整数类型

  • 取地址 & 功能: 获取变量的内存地址

操作数类型:左值(可修改的对象)

  • 间接引用 * 功能: 通过指针访问指向的值

操作数类型:指针类型

5.3类型相关单目操作符

  • 类型转换 (type) 功能: 将操作数显式转换为指定类型

操作数类型:任意可转换的类型

  • sizeof 操作符 sizeof 两种形式:
  1. sizeof(type):获取类型的大小
  2. sizeof expr:获取表达式结果类型的大小

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

这一节的内容跟数组指针的关系很大

6.1下标访问操作符[]

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

代码语言:javascript
复制
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[ ]的两个操作数是arr和9

6.2函数调用()

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

代码语言:javascript
复制
#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;
}

7.操作符的优先级、结合性

  • 优先级

优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是 不⼀样的。

代码语言:javascript
复制
3 + 4 * 5;

上⾯⽰例中,表达式 3 + 4 * 5⾥⾯既有加法运算符( +),⼜有乘法运算符(*)。由于乘法 的优先级⾼于加法,所以会先计算4 * 5,⽽不是先计算3 + 4

  • 结合性

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

代码语言:javascript
复制
5 * 6 / 2;

上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 , 再计算 / 2 。 运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概 记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了。

  • 圆括号( () )
  • ⾃增运算符( ++ ),⾃减运算符( – )
  • 单⽬运算符( + 和 - )
  • 乘法( * ),除法( / )
  • 加法( + ),减法( - )
  • 关系运算符( < 、 > 等)
  • 赋值运算符( = )

由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。

8.表达式求值

(1)整型提升

整型提升(integer promotion) 是指当表达式中使用比int类型小的整型(如char、short)时,这些值会被自动转换为intunsigned int类型后再参与运算的规则。

(2)整型提升的规则

有符号类型提升

如果原始类型的所有值都能用int表示,则提升为int,否则提升为unsigned int

  • char → int
  • short → int
无符号类型提升

如果原始类型的所有值都能用int表示,则提升为int,否则提升为unsigned int

  • unsigned char → int (如果int能表示所有unsigned char值)
  • unsigned short → int (如果int能表示所有unsigned short值)

(3)典例

代码语言:javascript
复制
#include <stdio.h>

int main() {
    char a = 120;
    char b = 20;
    char c = a + b;
    printf("%d\n", c);  // 输出 -116
    return 0;
}

为什么最后c的值是-116呢

这个问题涉及到 C语言中的整型提升溢出补码表示

  1. char 的存储范围
  • 在大多数C实现中,char 默认是 有符号的(signed char),范围是 -128 ~ 127。
  • 120 和 20 都在这个范围内,所以 a 和 b 的初始值是正确的。
  • 但 120 + 20 = 140,超过了 char 的最大值 127,导致 溢出(overflow)。
  1. 整型提升(Integer Promotion)
  • 在计算 a + b 时,C语言会先进行 整型提升:
  • a 和 b 都是 char 类型,比 int 小,所以会被提升为 int 类型。

提升后: a(120)→ int 类型的 120 b(20)→ int 类型的 20 计算 a + b 得到 140(仍然是 int 类型)。

  1. 赋值回 char 时发生截断 虽然 a + b 的结果是 140(int 类型),但我们要把它赋值给 char c,而 char 只能存储 -128 ~ 127。 所以 140 会被 截断(truncated) 以适应 char 的存储范围。

如何计算截断后的值? 140超出char的表示范围,计算方式如下:

  1. char 是 8 位有符号整数,能表示的最大值是 127(0b01111111)。
  2. 140 的二进制表示是 0b10001100(128 + 8 + 4 = 140)。
  3. 由于 char 是 有符号的,最高位是符号位:
  • 0b10001100 被解释为 补码(为什么是补码前面有讲过):

最高位 1 表示负数。

  1. 计算其补码对应的十进制值:

取反:0b10001100 → 0b01110011 加 1:0b01110011 + 1 = 0b01110100(116) 所以 0b10001100 表示 -116。

9.算术转换

由于某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型

(1)基本概念

  • 隐式转换:编译器自动进行的类型转换,无需程序员显式指定
  • 显式转换:程序员通过强制类型转换运算符明确指定的转换

(2)代码示例

代码语言:javascript
复制
int i = 5;
float f = 3.14;
double d = i + f; // i先转换为float,相加后再转换为double

unsigned int u = 10;
int s = -5;
long result = u + s; // s转换为unsigned int,可能导致意外结果

10.补充

这里我们把操作符一些残枝末节给收拾干净,避免给以后的学习留下盲点

(1)关系操作符

C 语⾔⽤于⽐较的表达式,称为 “关系表达式”,⾥⾯使⽤的运算符就称 为“关系运算符”

  • >⼤于运算符
  • <⼩于运算符
  • >=⼤于等于运算符
  • <=⼩于等于运算符
  • ==相等运算符
  • !=不相等运算符

关系表达式通常返回 0 或 1 ,表⽰真假。

注意: 相等运算符==与赋值运算符=是两个不⼀样的运算符,不要混淆。

为了防止出现这种错误,有的程序员喜欢将变量写在等号的右边。

代码语言:javascript
复制
if (3 == x) ... 1

这样的话,如果把 == 误写成 = ,编译器就会报错。

代码语言:javascript
复制
/* 报错 */
if (3 = x) ...
需要避免的错误是:多个关系运算符不宜连⽤。
代码语言:javascript
复制
i < j < k

上⾯示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果, 实际执行的是下面的表达式。

代码语言:javascript
复制
(i < j) < k 1

上⾯式⼦中, i < j 返回 0 或 1 ,所以最终是 0 或 1 与变量 k 进⾏⽐较。

如果想要判断变量 j的值是否在 i 和 k 之间,应该使⽤下⾯的写法。

代码语言:javascript
复制
i < j && j < k

(2)条件操作符

条件操作符也叫三⽬操作符,需要接受三个操作数的,形式如下:

代码语言:javascript
复制
exp1 ? exp2 : exp3

条件操作符的计算逻辑是: 如果 exp1 为真, exp2 计算,计算的结果是整个表达式的结果; 如果exp1 为假, exp3 计算,计算的结果是整个表达式的结果。

(3)短路

C语⾔逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。 如果左边的表达式满⾜逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为短路

代码语言:javascript
复制
if(month >= 3 && month <= 5) 

表达式中&& 的左操作数是 month >= 3 ,右操作数是 month <= 5 ,当左操作数 month >= 3 的结果是0的时候,即使不判断 month <= 5 ,整个表达式的结果也是0(不是春季)。 所以,对于&&操作符来说,左边操作数的结果是0的时候,右边操作数就不再执⾏。

  • 对于 || 操作符是怎么样呢?我们结合前⾯的代码:
代码语言:javascript
复制
if(month == 12 || month == 1 || month == 2) 1

如果month == 12,则不⽤再判断month是否等于1或者2,整个表达式的结果也是1。 所以,||操作符的左操作数的结果不为0时,就⽆需执⾏右操作数。

像这种仅仅根据左操作数的结果就能知道整个表达式的结果,不再对右操作数进⾏计算的运算称为短路求值

总结

由于这一章的很多内容小编已经在前面讲过了,所以有些内容会一笔带过 并且这一章的内容只是给C语言的分支与循环的程序练习作下铺垫

小编提醒 由于操作符的繁杂和多样性,所以在编程时候尽量不要写太乱的代码,以免程序发生错乱

如果你觉得这篇文章对你有帮助,请给文章一个三连支持一下哦

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.进制转换
    • (1)二进制
    • (2)八进制
      • 八进制位数与权重表
    • (3)16进制
      • 16进制权重计算表
    • (4)进制转换
      • 10进制转换成2进制
      • 2进制转换成8进制
      • 二进制转换成十六进制
  • 2.操作符的分类
  • 3.原码、反码、补码(了解即可)
  • 4.位操作符
    • 4.1移位操作符
      • <<左移操作符
      • >>右移操作符
      • 注意
    • 4.2按位操作符
      • 按位与&
      • 按位或|
      • 按位异或 ^
      • 按位取反~
    • 4.3复合赋值位操作符
    • 4.4位运算符使用技巧与练习
  • 5.单目操作符
    • 5.1算术类单目操作符
    • 5.2逻辑与位操作类单目操作符
    • 5.3类型相关单目操作符
  • 6.下标访问[]、函数调用()
    • 6.1下标访问操作符[]
    • 6.2函数调用()
  • 7.操作符的优先级、结合性
  • 8.表达式求值
    • (1)整型提升
    • (2)整型提升的规则
      • 有符号类型提升
      • 无符号类型提升
    • (3)典例
  • 9.算术转换
    • (1)基本概念
    • (2)代码示例
  • 10.补充
    • (1)关系操作符
      • 需要避免的错误是:多个关系运算符不宜连⽤。
    • (2)条件操作符
    • (3)短路
  • 总结
  • 如果你觉得这篇文章对你有帮助,请给文章一个三连支持一下哦
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档