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

目录

  • 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)短路
  • 总结
  • 如果你觉得这篇文章对你有帮助,请给文章一个三连支持一下哦

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 = 111 * 8^0
第2位8^1 = 822 * 8^1
第3位8^2 = 6433 * 8^2
第4位8^3 = 51244 * 8^3
第5位8^4 = 409655 * 8^4
第6位8^5 = 3276866 * 8^5
第7位8^6 = 26214477 * 8^6
第8位8^7 = 209715200 * 8^7

(3)16进制

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

16进制权重计算表

16进制位权重(16^n)示例值计算方式
016^0 = 10xF15 * 1
116^1 = 160xE14 * 16
216^2 = 2560xD13 * 256
316^3 = 40960xC12 * 4096
416^4 = 655360xB11 * 65536

(4)进制转换

10进制转换成2进制

请添加图片描述

2进制转换成8进制

请添加图片描述

二进制转换成十六进制

请添加图片描述

2.操作符的分类

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


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

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

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

请添加图片描述

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

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

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

4.位操作符

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

4.1移位操作符

<<左移操作符

移位规则:左边抛弃,右边补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. 算术右移:左边用原来该值的符号位填充,右边丢弃
#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

unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a & b; // 0001 (1)


按位或|

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

unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a | b; // 0111 (7)


按位异或 ^

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

unsigned char a = 5;    // 0101
unsigned char b = 3;    // 0011
unsigned char c = a ^ b; // 0110 (6)


按位取反~

0变1,1变0

unsigned char a = 5;    // 00000101
unsigned char b = ~a;   // 11111010 (250)




4.3复合赋值位操作符

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

  • 按位与赋值 &=
a &= b;  // 等价于 a = a & b;


  • 按位或赋值 |=
a |= b;  // 等价于 a = a | b;


  • 按位异或赋值 ^=
a ^= b;  // 等价于 a = a ^ b;


  • 左移赋值 <<=
a <<= n; // 等价于 a = a << n;


  • 右移赋值 >>=
a >>= n; // 等价于 a = a >> n;


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

  • 技巧
    不能创建第三个变量,从而实现两个临时变量的交换
#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下标访问操作符[]

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

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

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


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

  • 优先级

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

3 + 4 * 5;

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

  • 结合性

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

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)典例

#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)代码示例

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 ,表⽰真假。

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

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

if (3 == x) ... 1

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

/* 报错 */
if (3 = x) ...

需要避免的错误是:多个关系运算符不宜连⽤。

i < j < k

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

(i < j) < k 1

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

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

i < j && j < k


(2)条件操作符

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

exp1 ? exp2 : exp3

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


(3)短路

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

if(month >= 3 && month <= 5) 

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


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

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

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


总结

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

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


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

在这里插入图片描述

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值