【C语言初阶(NEW)】五、操作符详解(二)|隐式类型转换|算术转换|操作符的属性

news/2025/2/21 8:05:43/

目录

一、表达式求值

1.1 隐式类型转换

1.1.1 什么是整型提升(整型提升)

1.1.2 整型提升的意义

1.1.3 有符号(signed)与无符号(unsigned)的区别

1.1.4 有符号(signed)类型的整型提升

1.1.5 无符号(unsigned)整形提升

1.1.6 整型提升例子

1.2 算术转换

1.3 操作符的属性

二、位操作符的一些例题

三、数据类型大小


一、表达式求值

        表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

1.1 隐式类型转换

1.1.1 什么是整型提升(整型提升)

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

1.1.2 整型提升的意义

        表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

        因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度

        通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值都必须先转换为int或unsigned int,然后才能送入CPU去执行运算

1.1.3 有符号(signed)与无符号(unsigned)的区别

        首先在计算机中,有符号数是可以用来区分数值的正负,而无符号数仅有正值,没有负值

         其次当一个数是无符号数时,它的最高位仅用来表示该数的大小。而当一个数是有符号数时,此时的最高位称为符号位;该符号位为1时表示该数为负值,为 0 时则表示为正值

        最后有符号数和无符号数两者表示的范围不同,即同样长度的字节,有符号数比无符号数的最大值出现缩水

二者最明显的区别就是二者表示的范围不同:

  • 无符号数中,所有的位都用于直接表示该值的大小
  • 有符号数中最高位用于表示正负,所以,当为正值时,该数的最大值就会变小

1.1.4 有符号(signed)类型的整型提升

有符号类型整形提升的时候,高位补充符号位,即为 0 或 1

假设平台都是 32位 

1)正数的整形提升

char b = 1;
1 的补码是:00000000 00000000 00000000 00000001
变量b 的类型是char,二进制位(补码)中只有8个比特位(一个字节):
b 在内存中存储为:00000001(补码截取8个bit)因为 char 为有符号的 char,如果 b 进行整型计算,就会发生整型提升
整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000 00000000 00000000 00000001

         简单说一下,char 虽然是字符类型,但字符类型储存的时候,储存的是字符的ASCII码值,ASCII值是整数,所以 char 的存储依旧与整型存储一样

        char 在我当前的编译器 vs2019 是有符号型,不同的编译器会有不同的情况,因为 char 在C语言中未定义为有符号还是无符号,有符号还是无符号取决的编译器

2)负数的整型提升


char b = -1;
-1 的补码是:11111111 11111111 11111111 11111111
变量b 的类型是char,二进制位(补码)中只有8个比特位(一个字节):
b 在内存中存储为:11111111(补码截取后8个bit)因为 char 为有符号的 char,如果 b 进行整型计算,就会发生整型提升
整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111 11111111 11111111 11111111

1.1.5 无符号(unsigned)整形提升

有符号类型整形提升的时候,高位补充 0

char b = -1;
-1 的补码是:11111111 11111111 11111111 11111111
变量b 的类型是char,二进制位(补码)中只有8个比特位(一个字节):
b 在内存中存储为:11111111(补码截取后8个bit)因为 char 为无符号的 char,如果 b 进行整型计算,就会发生整型提升
整形提升的时候,高位补 0
提升之后的结果是:
00000000 00000000 00000000 11111111

 之上简而言之,有符号与无符号的整型提升的区别就是

有符号的整型提升高位补 符号位

无符号的整型提升高位补 0

1.1.6 整型提升例子

例子1: 

测试代码

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

运行结果是:-126

原因解释如下:

首先,我们先看 3是一个整型,占 4 个字节,32 个bit位;127 也是如此,占4个字节 

3 和 127 的二进制原码的补码

  3的补码:00000000 00000000 00000000 00000011
127的补码:00000000 00000000 00000000 01111111
(正数的原、反、补相同)

        int类型是 4 个字节,32 个bit位,而char类型只能储存 1 个字节,也就是 8 个 bit 位,所以 char 会发生截断,即选择 32 位中的最低位放入 char 中,也就是后八位

所以 3 和 127 在char类型中只能储存 8 个 bit,如下(补码)

  3:00000011
127: 01111111

接下来 a 和 b 是如何相加的

        a 和 b自身的大小都是8个比特位占一个字节(char类型),没有达到一个整型的大小(4字节)。在计算时为了能够提升计算精度,要将a和b整型提升

整型提升是按照变量的数据类型的符号位来进行提升的

a 和 b,a 和 b 现在是有符号的字符型变量,最高位0是它的符号位。高位通通补0,补全32个比特位

a整型提升前:00000011 //最高位的0是它的符号位
a整型提升后:00000000 00000000 00000000 00000011
b整型提升前:01111111 //最高位的0是它的符号位
b整型提升后:00000000 00000000 00000000 01111111

 整型提升后 a 和 b 进行二进制相加,结果为

a+b:00000000 00000000 00000000 10000010

现在要将结果放入 c 中,而c又是 char 型,又要发生截断

c:100000010

 此时还不能直接打印输出,因为 printf 函数中是以 %d 的形式进行打印,又要对 c 进行整型提升

c 的类型是有符号字符型,最高位 1 为他的符号位,高位通通补1,补全32个比特位

c整型提升前:10000010
c整型提升后:11111111 11111111 11111111 10000010

此时 c 得到的是补码,还要反推原码才能打印

c的补码:11111111 11111111 11111111 10000010
c的反码:11111111 11111111 11111111 10000001
c的原码:10000000 00000000 00000000 01111110

接下来二进制转换成十进制就可以打印 c 了,结果是 -126

例子2:

测试代码

#include <stdio.h>
int main()
{char a = 0xb6;short b = 0xb600;int c = 0xb6000000;if (a == 0xb6)printf("a");if (b == 0xb600)printf("b");if (c == 0xb6000000)printf("c");return 0;
}

运行结果

解释: 

        a,b要进行整形提升,但是c不需要整形提升;a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真,所以最后打印 c

例子3:

#include <stdio.h>
int main()
{char c = 1;printf("%d\n", sizeof(c));printf("%d\n", sizeof(+c));printf("%d\n", sizeof(-c));return 0;
}

 运行结果

解释:

        c 只要参与表达式运算,就会发生整形提升,表达式 +c 就会发生提升,所以 sizeof(+c) 是4个字节。表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

 ----------------我是分割线---------------   

1.2 算术转换

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

下面的层次体系称为寻常算术转换

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

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算,简单来说就是:从下往上转换。比如,a 的类型是 int,b 的类型是 double,进行 a+b 是,int 就会转换成 double 类型,再进行计算

例如

#include<stdio.h> int main()
{int a = 3;float b = 3.5;float c = a + b;//算术转换,int-->float //结果为 6.5return 0;
}

 调试查看:

注意区分:整型提升是不足 4 个字节才会发生整型提升,像 char(1字节) short(2字节)...(32位平台下),这些就会发生整型提升。如果超过了4个字节的不同类型计算,就会发生算术转换,向高位进行转换

警告:但是算术转换要合理,要不然会有一些潜在的问题

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

  ----------------我是分割线---------------   

1.3 操作符的属性

复杂表达式的求值有三个影响的因素

  • 1. 操作符的优先级
  • 2. 操作符的结合性
  • 3. 是否控制求值顺序

两个相邻的操作符先执行哪个?

取决于他们的优先级,如果两者的优先级相同,取决于他们的结合性

优先级图表

 

 

 

结合性说明:N/A 意思是同级运算没有顺序要求;L-R 意思是同级运算有顺序要求,方向为自左向右;R-L 意思是同级运算有顺序要求,方向为自右向左

注意:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的 

二、位操作符的一些例题

1、编写代码实现:求一个整数存储在内存中的二进制中1的个数

思路:用位操作符解决比较优,也可以使用其它方法 

int a = 3;
00000000000000000000000000000011a&1
00000000000000000000000000000011
00000000000000000000000000000001
00000000000000000000000000000001
//>> <<

代码如下:

#include <stdio.h>
int main()
{int num = -1;int i = 0;int count = 0;//计数for (i = 0; i < 32; i++){if (num & (1 << i))count++;}printf("二进制中1的个数 = %d\n", count);return 0;
}

2、不能创建临时变量(第三个变量),实现两个数的交换

思路:使用位操作解决

3^3 = 0 -> a^a=03:0113:011
结果:0000^5=5 -> 0^a = a
000
101
1013^3^5 = 5
3^5^3 = 5
结论:异或操作符支持交换律

代码如下:

#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;
}

三、数据类型大小

char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数

代码如下:

#include <stdio.h>
int main()
{printf("%d\n", sizeof(char));printf("%d\n", sizeof(short));printf("%d\n", sizeof(int));printf("%d\n", sizeof(long));printf("%d\n", sizeof(long long));printf("%d\n", sizeof(float));printf("%d\n", sizeof(double));printf("%d\n", sizeof(long double));return 0;
}

运行结果(32位平台)

文章就到这里 


http://www.ppmy.cn/news/304.html

相关文章

鲲鹏devkit性能分析工具介绍(四)

鲲鹏devkit性能分析工具介绍&#xff08;四&#xff09; 前面我们已经介绍了鲲鹏devkit性能分析工具的全景分析、热点函数分析、进程/线程分析、微架构分析、和访存分析&#xff0c;由此可见进行性能调优绝对不能够仅仅去进行一方面的考察而是需要全方面的数据分析进行一定的舍…

互联网摸鱼日报(2022-11-30)

互联网摸鱼日报&#xff08;2022-11-30&#xff09; InfoQ 热门话题 2022 亚马逊云科技re:Invent&#xff1a;首日现场花絮 2022亚马逊云科技re:Invent&#xff1a;世界开发者说 聚焦增长&#xff0c;探索 RTC 在互娱应用的新进化 云中的 MySQL 是 DBA 的终结吗&#xff1f…

Vue源码学习(六)- 实例方法

目标 深入理解以下实例方法的实现原理 vm.$setvm.$deletevm.$watchvm.$onvm.$emitvm.$offvm.$oncevm._updatevm.$forceUpdatevm.$destroyvm.$nextTickvm._render 入口 /src/core/instance/index.js 该文件是Vue实例的入口文件&#xff0c;包括Vue构造函数的定义&#xff0c;…

深度学习与总结JVM专辑(六):JVM字节码执行引擎

JVM字节码执行引擎前言运行时栈帧结构方法调用解析虚方法和非虚方法分派静态分派静态类型和实际类型动态分派字段没有多态性单分派和多分派JVM动态分派的实现前言 执行引擎是JVM核心的组成部分之一。 “虚拟机”是一个相对于“物理机”的概念&#xff0c;这两种机器都有代码执…

C语言百日刷题第十天

前言 今天是刷题第10天&#xff0c;放弃不难&#xff0c;但坚持一定很酷~ 快来跟我一起刷题吧。 C语言百日刷题第十天前言81.连接两个字符串82.输入一行字符&#xff0c;分别统计其中英文字母、空格、数字和其他字符的个数。83.写一个排序函数实现数组从小到大的排序84.字母的大…

Python学习:optparse模块

optparse&#xff0c;是一个更够让程序设计人员轻松设计出简单明了、易于使用、符合标准的Unix命令例程式的Python模块&#xff0c;生成使用和帮助信息。 参数说明 dest&#xff1a;用于保存输入的临时变量&#xff0c;其值通过options的属性进行访问&#xff0c;存储的内容是…

MuLogin的WebRTC功能介绍与设置

WebRTC 协议可以绕过代理取到一些本机的网卡IP和真实的上网公网IP地址&#xff0c;那么我们可以使用替换模式来让网站取到我们指定的IP信息&#xff0c;或用禁用模式&#xff0c;让被访问的网站不能通过WebRTC协议来取我们的IP地址。这里如果你不知道公网IP是多少&#xff0c;建…

安科瑞安全用电监测,智慧用电装置,导轨式安装带无线通讯功能

安科瑞 王晶淼/司红霞 前言 随着电气化的迅猛发展和用电普及程度的日益提高,电已经成为人类生存和发展必不可少的能源之一。然而,由于种种原因,由电气引发的火灾和爆炸事故也直呈现上升趋势。电气设备的绝缘大量使用塑料、橡胶、绝缘漆、稀释剂等易燃物品,在电气设备运行中,由…