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

彻底搞懂操作符:C语言表达式核心手册

C语言:操作符

  • 一.进制转换
    • 1.有哪些进制
    • 2.如何转换
  • 二.原码、反码、补码(二进制)
    • 1.有符号
    • 2.无符号
  • 三.位移操作符(<< >>)
    • 1.左移操作符( << )
    • 2.右移操作符( >> )
  • 四.位操作符(& | ^ ~)
  • 五.逗号表达式
  • 六.下标访问( [ ] )和函数调用( ( ) )
  • 七.结构成员访问操作符
    • 1.什么是结构体以及结构体的声明
    • 2.如何结构体变量的定义以及初始化
    • 3.结构体成员直接访问和间接访问
      • 3.1直接访问
      • 3.2间接访问
  • 八.操作符的优先级和结合性
    • 1.优先级
    • 2.结合性
      • 2.1左结合
      • 2.2右结合
  • 九.表达式求值
    • 1.整形提升
      • 1.1何为整型提升
      • 1.2整型提升的意义
      • 1.3如何整型提升
    • 2.算术转换

一.进制转换

1.有哪些进制

二进制:是由01两个数组成,满2进1;例如4的二进制为:0100
八进制:是由01两个数组成,满8进1;例如10的八进制为:012
十进制:是由0~9组成的,也是我们平常算数所用的
十六进制:是由0至9,a至f组成,满16进1;例如30的十六进制为:1b

那如何将这些进制进行转换呢?

2.如何转换

我们可以利用二进制当中间枢纽,例如八进制转十六进制,可以先将八进制转换为二进制,再将二进制转换为十六进制。

如八进制:0153转换成十六进制
首先转换为二进制,因为我们知道,八进制的每一位都相当于二进制的三位,例如0153中的3转换二进制是011,同理0153中的5转换二进制是101,0153中的1转换二进制是01,按排列从右到左可得八进制0153的二进制为01101011;

0 11 0 10 1 1
153

再将二进制01101011转换为十六进制,因为十六进制的每一位相当于二进制的四位,例如01101011中的1011转换十六进制是b,01101011中的0110转换十六进制是6,得到二级制01101011转换成十六进制为6b;

0 1 1 01 0 1 1
6b

最后我们得到八进制0153转换成十六进制为6b。

总结:某进制—>二进制—>想要的进制

二.原码、反码、补码(二进制)

整数的二进制表示法有三种,即原码、反码和补码;分为两类,一类是有符号的,另一类是无符号的。

原码

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

反码

将原码的符号位不变(先看有没有符号位),其他位依次按位取反就可以得到反码。

补码

反码+1就得到补码。

1.有符号

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

例如(取32位):

原码00000000000000000000000000000101
反码111111111111111111111111111111111010
补码111111111111111111111111111111111011

2.无符号

无符号整数的三种 2 进制表示相同,没有符号位,每⼀位都是数值位。

例如(取32位):

原码00000000000000000000000000000101
反码00000000000000000000000000000101
补码00000000000000000000000000000101

对于整形来说:数据存放内存中其实存放的是补码。
缘由:

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

三.位移操作符(<< >>)

移位操作符的操作数只能是整数。

1.左移操作符( << )

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

如图所示:

补码00000000000000000000000000000101
过程<--00000000000000000000000000000101<--0
结果00000000000000000000000000001010

代码参考:

#include<stdio.h>intmain(){intnum=10;intn=num<<1;printf("n = %d\n",n);printf("num = %d\n",num);return0;}

2.右移操作符( >> )

移位规则:首先右移运算分两种:
1.逻辑右移:左边用0填充,右边丢弃。
2.算术右移:左边用原该值的符号位填充,右边丢弃。

逻辑右移:

补码11111111111111111111111111111111
过程0-->11111111111111111111111111111111-->
结果01111111111111111111111111111111

算术右移:

补码11111111111111111111111111111111
过程1-->11111111111111111111111111111111-->
结果11111111111111111111111111111111

代码参考:

#include<stdio.h>intmain(){intnum=10;intn=num>>1;//int n = num >> -1;对于移位运算符,不要移动负数位,这个是标准未定义的,所以这种写法是错误的printf("n = %d\n",n);printf("num = %d\n",num);return0;}

四.位操作符(& | ^ ~)

位操作符的操作数必须是整数。

写一个小代码,如下所示:

#include<stdio.h>intmain(){intnum1=-3;intnum2=5;printf("%d\n",num1&num2);printf("%d\n",num1|num2);printf("%d\n",num1^num2);printf("%d\n",~0);return0;}

运行结果:

我们可以看到
num1 & num2 = num2
num1 | num2 = num1
num1 ^ num2 = -1
~0 = -1

这是如何计算的呢?
这就要结合前面所说的补码以及位操作符各自的计算规则了,最后返回原码。

按位与 &

规则:两位都为 1,结果才为 1

补码
-31101
50101
结果0101

num1 & num2 == num2

按位或 |

规则:有一个为 1,结果就是 1

补码
-31101
50101
结果1101

num1 | num2 == num1

按位或异 ^

规则:相同为 0,不同为 1

补码
-31101
50101
结果1000

其中1是符号位
最后得num1 ^ num2 == -8

按位或异 ~

规则:0 变 1,1 变 0

所以~0 == 1; ~1 == 0;

五.逗号表达式

逗号表达式就是用逗号隔开的多个表达式,从左向右依次执行

表达式1,表达式2,表达式3,...,表达式n

整个表达式的结果是最后⼀个表达式的结果。

例如:

inta=(3+4,5*2,10-3);
  1. 先算 3+4 --> 7(结果丢弃)
  2. 再算 5*2 --> 10(结果丢弃)
  3. 最后算 10-3 --> 7
    所以:a = 7

六.下标访问( [ ] )和函数调用( ( ) )

这在之前数组和函数分别有讲,就不再罗嗦了,直接看代码参考一下:

下标访问 [ ]

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

函数调用 ()

#include<stdio.h>ret_typefun_name()//ret_type-->函数返回类型{//函数体}ret_typefun_name1(形式参数)//ret_type-->函数返回类型{//函数体}intmain(){fun_name();//这⾥的()就是作为函数调用操作符fun_name1(形式参数);//这⾥的()就是函数调用操作符。//函数名return0;}

七.结构成员访问操作符

1.什么是结构体以及结构体的声明

结构体就是 C 语言里,你自己定义的一种 “复合型数据类型”,可以把多个不同类型的变量打包在一起。

结构体的声明

structtag{member-list;}variable-list;

注意:其中variable-list可以省略,但是;不可以

举个例子,一个学生有他自己的学号(id)、姓名(name)、年龄(age)、成绩(score)等等;将这些单独定义变量(id、name、age、score)用结构体就能打包成一个整体,如下所示:

//定义一个结构体类型structStudent{intid;//学号charname[20];//姓名intage;//年龄floatscore;//成绩};

2.如何结构体变量的定义以及初始化

定义结构体变量的定义

structStudentstu1;

直接初始化

structStudentstu1={007,"张三",18,95.5};

3.结构体成员直接访问和间接访问

3.1直接访问

根据上述来写一简单代码展示结构体,如下所示:

#include<stdio.h>structStudent{intid;//学号charname[20];//姓名intage;//年龄floatscore;//成绩};intmain(){// 定义结构体变量并赋值structStudentstu={007,"张三",18,92.5};printf("学号:%d\n",stu.id);printf("姓名:%s\n",stu.name);printf("年龄:%d\n",stu.age);printf("成绩:%.1f\n",stu.score);return0;}

其中结构体成员的直接访问是通过点操作符(.)访问的,例如stu.id直接访问struct Student stu中的007

3.2间接访问

如果是指针,则需要间接访问其对应的地址,用->

#include<stdio.h>structStudent{intid;charname[20];intage;floatscore;};intmain(){structStudentstu={1001,"小李",19,88.5};structStudent*p=&stu;printf("学号:%d\n",p->id);printf("姓名:%s\n",p->name);printf("年龄:%d\n",p->age);printf("成绩:%.1f\n",p->score);return0;}

由此我们可以看出
结构体变量:用.stu.id
结构体指针:用->p -> id(p -> id本质等价于(*p).id)

八.操作符的优先级和结合性

1.优先级

操作符优先级就是代码里的 “先算谁、后算谁”,和数学里 “先乘除后加减” 是一个道理。

例如程序代码中的int sum = (a+b) * c,()的等级比*高,所以(a+b)先运算,最后在乘c

#include<stdio.h>intmain(){inta=2,b=3,c=4;intsum=(a+b)*c;printf("%d\n",sum);return0;}

算出的结果是20,而不会是别的。

优先级表

==>相关链接

2.结合性

优先级相同的时候,从左往右算(左结合),还是从右往左算(右结合)。

2.1左结合

算术:+-*/%
关系:<<=>>===!=
逻辑:&&||
移位:<<>>
逗号:,

例如:

a-b-c;//加减乘除

等价于:(a - b) - c

2.2右结合

一元运算符:!++--+ -(正负号)
赋值运算符:=+=-=*=/=
三元运算符:? :

例如:

a=b=c=10;//赋值

等价于:a = (b = (c = 10))

a?b:c?d:e;//三元运算符

等价于:a ? b : (c ? d : e)

九.表达式求值

1.整形提升

1.1何为整型提升

整型提升是 C 语言中小于int的整型在表达式中被自动隐式转换为int/unsigned int的规则,是表达式求值的前置步骤,目的是提升运算效率、避免精度丢失。

核心规则:

  1. 若原类型所有值能被int表示 → 提升为int
  2. 否则–>提升为unsigned int
  3. 有符号类型:符号扩展(高位补符号位)
  4. 无符号类型:零扩展(高位补 0)

1.2整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purposeCPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为intunsigned int,然后才能送入CPU去执行运算。

例如:

chara,b,c;...chara=b+c;

bc的值被提升为普通整型(int),然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于a中。

1.3如何整型提升

举个例子,先看下面一段代码,算算它的结果是多少:

#include<stdio.h>intmain(){chara=20;charb=120;charc=a+b;printf("%d\n",c);return0;}

第一直觉会告诉你,等于140,那就把问题想得太简单了,最终运行结果答案是-116:

这是为什么呢?

整形提升规则

  1. 有符号整数提升是按照变量的数据类型的符号位来提升的;
  2. 无符号整数提升,高位补0。
#include<stdio.h>intmain(){chara=20;//截断后存储到a//000000000000000000000000'00010100'-->补码//00010100charb=120;//000000000000000000000000'01111000'-->补码//011111000charc=a+b;//000000000000000000000000'00010100'//000000000000000000000000'01111000'//000000000000000000000000'10001100'//000000000000000000000001'10001100'//10001100--->cprintf("%d\n",c);//111111111111111111111111'10001100'-->补码//100000000000000000000000'01110011'//100000000000000000000000'01110100'-->原码//-116return0;}

2.算术转换

在上面讲了操作符的优先性和结合性以及整型提升,那我们的算术转换就会更好理解了。

算术转换就是如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。

寻常算术转换(从大到小依次排列):

1.longdouble2.double3.float4.unsignedlongint5.longint6.unsignedint7.int

如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外⼀个操作数的类型后执行运算

http://www.jsqmd.com/news/639088/

相关文章:

  • Agent 的版本迭代策略:渐进式升级还是推倒重来
  • 联合查询
  • MySQL 死锁问题分析与解决
  • HY-MT1.5翻译模型快速入门:基于星图镜像的部署与测试
  • 升鲜宝生鲜配送供应链管理系统源代码——CRM模块功能设计(二)
  • Modern.js 3.0 正式发布:更聚焦的 Web 框架,全面拥抱 Rspack 与 RSC
  • 日常测试工程稳定保证流程
  • AllData数据中台通过集成开源项目Apache IOTDB Web相关项目,建设物联网数据库平台
  • HY-MT1.5-7B镜像使用指南:Jupyter Lab调用与常见问题解决
  • LiuJuan20260223Zimage多模态潜力展望:从文本到未来图像与代码生成
  • Pi0 LeRobot 0.4.4兼容性部署指南:解决PyTorch版本冲突实操
  • 测试人员必看:避开这5个职业陷阱,升职加薪不是梦
  • 一文速成!Pytest自动化框架面试题,帮你全部搞定!
  • ucharts堆叠柱状图实战:如何动态调整Y轴范围让零线居中(附完整代码)
  • 创建SpringWeb项目(Spring2.0)
  • 漫谈上海比较好的美国移民公司,价格与专业度的考量 - 工业品牌热点
  • linux下语言设置
  • CAN知识大全——带你读懂CAN数据手册
  • MobileNetSSD_deploy.caffemodel下载地址
  • LightOnOCR-2-1B快速上手:使用LightOnOCR-2-1B构建微信小程序OCR接口
  • ChatGPT 深度实测:到底有没有?功能全不全?版本新不新?
  • AI智能体视觉检测系统(TVA)工作原理系列(十二)
  • 瑞祥商联卡回收全攻略,高效又安全! - 团团收购物卡回收
  • 别再盲目找!适合你的CRM系统,从这几个选项开始选 - 毛毛鱼的夏天
  • 软件工程师如何5年内薪资翻倍?真实案例揭秘
  • 黑丝空姐-造相Z-Turbo项目文档自动化:用LaTeX排版生成报告与论文
  • 如何在Windows上免费创建10个虚拟显示器:终极解决方案指南
  • 2026年靠谱的婚姻律师推荐,宁波处理非婚生子女抚养纠纷律师哪家好 - 工业品网
  • 【Hyper-V】Windows11家庭版如何解锁并启用Hyper-V功能
  • 长芯微LPA8421完全P2P替代AD8421,是一款低成本、低功耗、极低噪声、超低偏置电流、高速仪表放大器