【探究】C语言-类型转换问题
25年9月团队招新,自己带了一些小家伙,帮助他们进行C语言的学习,同时对他们的疑惑进行解答。
其中,有一个小家伙的问题很有意思,是初学者不尝思考、老家伙(我)也不一定清晰知道的点,涉及到数据的储存和转换方式,比较有趣,于是查阅资料学习后写下此文
关键词
浮点数的数据储存方式
变量的自动转化
前言
为了探究变量自动转换的问题,我们首先需要知道为什么程序中需要有变量类型的存在?
我们知道,人类发明的目的是为了更好得改善生活体验,其创造出的每个东西都有其存在的目的和价值。发明文字,是为了更好去进行个体交流;发明纸笔,是为了更好去传承经验和文明;而对于编程,其诞生的目的就是描述现实中的问题并丢给计算机快速解决。比如说,小明想要尽快搭车去学校,对于这个问题,由于选取的路线、车辆不同,转场时机不同,最后的结果可能有多种。对于人而言,一个个去模拟计算这个过程耗时极大的。而编程就是用计算机语言去描述这个问题,再利用计算机高效的处理效率快速解决问题。
为了描述这个问题,我们需要先创建问题的主体(小明)以及主体作用的对象(学校)和行为(搭校车)。这里的主体和作用对象就是程序中需要声明的变量,而行为就是程序另外一个重要组成部分——函数。
由于现实中对象的特征不同,因此转化为计算机变量对应的类型也不一样,常见的种类大致可以划分为:整型变量类型、字符串变量类型、浮点变量类型。本文主要围绕的,即是整型变量和浮点变量间的关系
变量的储存方式
我们都知道,计算机处理器的核心组件为二极管,本质上只能表示和处理二进制数据。而我们现在接触的编程语言之所以能够表示更加复杂的内容,是因为经过了计算机操作系统等软硬件的层层封装,把底层的二进制实现给隐藏起来。知道变量的二进制储存形式,将会对我们的代码理解、算法设计带来巨大的好处。
计算机上的所有文件本质都是一串01二进制码,变量也不例外。
在学习字符串部分,我们不难知道,在相同编码规范下的字符,其实际储存的数值和真实数据之间是一个一一映射的关系。拿ASCLL编码举例,字符a对应数值97,二进制数值01100001(8bit,1Byte),于是我们可以通过这样的映射规则去利用二进制编码储存我们想要的数据。
整型变量类型和浮点变量类型都是数的类型,本身组成部分就可以用二进制来表示,因此不需要实际数据和储存数据之间的转化关系表,只需要处理其中的正负值、小数值即可。
整型变量的储存方式
按照需求,整型变量需要能表示一定范围内的整数,包括正数以及负数。正数可以直接用二进制码进行表示,而为了解决负数表示的问题,编程语言开发者引入了一个补码的形式
补码
补码的目的是实现正负数的储存、将减法转变为加法以及统一正负数的储存与运算,转换规则如下:
正数补码:与原码相同
例如:十进制 5
(假设用 8 位存储)
原码 = 补码 = 00000101
负数补码:先取其绝对值原码,再按位取反,最后加1
例如:十进制 -5
(8 位存储)
绝对值原码:00000101
反码:(按位取反):11111010
补码(反码 + 1):11111011
按位取反后的反码与源码相加能溢出归零,因此这里的取反相当于取了一个可消去原码数值的”负数,实现了负数的表示(类比自然数中的相反数定义:两个数相加等于零,则一个数就是另一个数的负值)
同时,取反前后的二进制码普遍的特点是第一个二进制位正数为0,负数为1。为了统一规范,定第一个二进制位为符号位,因此,n字节的整型变量可表示范围为
$-2^{(n-1)} \sim 2^{(n-1)}-1$
$Range : -32768 \sim 32767$
int 变量类型对应4字节空间,可以储存的整数数范围为:
$Boundary = 2^{(n-1)} =2^{ (4*4-1)} = 2^{15} = 32768$
浮点数储存方式
因为本人也没有系统性了解浮点数储存方式,因此暂贴一篇文章,留待后面进行补充
参考资料:zhuanlan.zhihu.com
变量的自动转化
在计算机编程中,时常会遇到不同类型的数值变量计算问题。对于这个问题,理论上应该是先把所有参与运算的变量统一为标准类型,然后再进行计算,确保计算方式以及存储精度的统一。
不过,刚刚学习C语言的小家伙往往意识不到变量不统一带来的问题,经常出现计算表达式中int、float、double互用的场景,但又能正常运行,这是什么原因呢?
万能的编译器
现代编译器设计十分完备,在代码进行编译前会进行错误检查。对于一些常见的错误,编译器会自动进行纠正,例如这里的变量类型转换。
从编写程序代码到在机器上运行,其中不可或缺的步骤就是利用编译器进行编译(链接)。编译器编译过程大致分为以下几个阶段:
词法分析
语法分析
语义分析
中间代码生成
代码优化目标代码生成
其中,语义分析到目标代码生成过程,编译器会自动检查并进行必要的类型转换。在语义分析阶段,编译器会检查抽象语法树,检查表达式中的操作数的数据类型是否匹配,如果不匹配,则触发转换逻辑。
触发转换逻辑后,编译器首先会检查是否符合语言的隐式转换规则(如“拓宽转换”允许int → float,但是“缩窄转换可能被禁止或者警告,如flota → int),然后在生成中间代码时插入专门的转换指令
编写代码时可以注意制定正确的变量类型,避免不必要的类型转换指令开销
允许的转换规则
编译器支持显式转换和隐式转换两种方式。显式转换即程序员在代码中使用("指定转换类型")
将变量转换为指定类型。
char ch = 'a';
int temp;
temp = (int)ch;
// temp 接收'a'对应转换为int型变量的值
// 字符 --> 整型的值对应字符对应的ASCLL码值
// 'a'对应ASCLL码表值为97,因此这里temp值对应更新为97
由于这种类型转换是程序员手动进行的,程序员知悉其转换对应带来的信息损失,评估并解决其潜在问题,因此编译器相信程序员的选择,不会出现问题报错。
隐式转换即程序员没有指定变量数据类型转换,但在程序计算部分需要进行数据转换的地方,编译器会根据一定规则进行安全的自动转换。
隐式数据类型转换一般遵循“大范围类型” --> “大范围类型”的转换,避免数据丢失。
这里的“范围”可以看做变量能够蕴含的信息量。例如,整型变量只能表达整数,而浮点型变量可以表达所有实数,因此范围:浮点型 > 整型
当程序需要进行“大范围类型”-->“小范围类型”的隐式操作时,编译器不确定这样的转换操作带来的信息缺失/变换是否会对程序产生危害,因此输出错误报错,提示程序员可能存在的问题,有程序员定夺是否进行变量转换。
常见允许的转换规则如下:
拓宽转换:
bool → char/int/long
char → short/int/long
int → long/float/double
float → double
缩窄转换
int → char
注意,待转换变量值需要在目标类型变量的取值范围内。例如,char接受(-128~127)范围内的值,则待转换的int类型变量值不能超过这个范围,否则会报错
总结
总而言之,编译器在编译程序的过程中需要确保生成的二进制文件在运行时不会出现重大错误,因此编译器对于隐式转换错误部分会直接产生报错退出,而对于程序员手动操作的显式转换部分,则相信程序员的设计不进行报错。理解这个point也就不难理解显式/隐式类型转换之间的关系。
在计算机编程中我们经常会遇到类似难以理解区分的概念,此时我们不妨去了解一下概念底层相关的计算机软硬件设计。在了解这些设计过程中你一定会对原有的问题和概念有深刻而清晰的认知。毕竟,现在计算机理论应用都是基于计算机体系机构一层层的封装与继承,了解底层,就掌握了程序运行的心脏。
参考文献
【探究】C语言-类型转换问题的更多相关文章
- 探究C语言中的前++和后++
小波带您探究c语言中的前++与后++: 欢迎吐槽,欢迎加QQ463431476. 欢迎关注! 现在来探究: 咱们先看第一个 i被赋值0,i++(后++)并没有输出1. 现在i被赋值0,++i,也 ...
- Go 语言类型转换
类型转换用于将一种数据类型的变量转换为另外一种类型的变量.Go 语言类型转换基本格式如下: type_name(expression) type_name 为类型,expression 为表达式. 实 ...
- Go语言类型转换
类型转换用于将一种数据类型的变量转换为另外一种类型的变量. Go语言类型转换基本格式如下:表达式 T(v) 将值 v 转换为类型 T . Go语言各种类型转换及函数的高级用法:strconv包实现了基 ...
- GO语言学习(十七)Go 语言类型转换
Go 语言类型转换 类型转换用于将一种数据类型的变量转换为另外一种类型的变量.Go 语言类型转换基本格式如下: type_name(expression) type_name 为类型,expressi ...
- Delphi VS C语言类型转换对照
Delphi VS C语言类型转换对照 When converting C function prototypes to Pascal equivalent declarations, it's ...
- C语言类型转换原理
C语言类型转换 int a; a=1.23 这里把1.23赋值给a发生了隐式转换,原理如下: int a; float b=3.14; a=b; b赋值给a的过程:首先找一个中间变量是a的类型(该例中 ...
- c语言类型转换注意事项
转载自: http://blog.csdn.net/zhuimengzh/article/details/6728492 1.隐式转换 C在以下四种情况下会进行隐式转换: 1.算 ...
- C语言---类型转换
itoa 功 能:把一整数转换为字符串 用 法:char *itoa(int value, char *string, int radix); 详细解释:itoa是英文integer to array ...
- 【揭秘】C语言类型转换时发生了什么?
ID:技术让梦想更伟大 作者:李肖遥 链接:https://mp.weixin.qq.com/s/ZFf3imVaJgeesuhl1Kn9sQ 在C语言中,数据类型指的是用于声明不同类型的变量或函数的 ...
- Go语言|类型转换和类型别名
类型转换 同类型之间的转换 Go语言中只有强制类型转换,没有隐式类型转换.该语法只能在两个类型之间支持相互转换的时候使用. import "fmt" func main() { v ...
随机推荐
- windows镜像esd转iso
背景 经常在三方网站(比如:修系统.不忘初心系统)下载到精简系统,但是这些系统的格式不仅仅是iso,还有可能是esd. 虽然两者几乎等价,但是有些平台 比如虚拟机.mac转换助理不能识别esd格式的镜 ...
- pdf渲染和对比 react-pdf-highlighter
前言 react-pdf-highlighter 使用此插件 做pdf的预览 高亮 批注 对比等 效果 地址 https://github.com/dingshaohua-cn/pdf-highlig ...
- 批量生成测试数据,再次迎来升级,支持API调用,开发者的好帮手
前端时间发表一篇文章介绍了FabricateData的在线批量生成测试数据的能力,这几天在看,平台不仅添加了本地数据源的概念,还增设了本地API的能力. FabricateData 网站地址:http ...
- screen 相当于 浏览器的多标签.简单使用说明
简介:screen 相当于 浏览器的多标签. 转载: CSDN 个人常用命令 screen -ls 显示所有视窗 杀死视窗 kill -9 threadnum 例如在上面的2637,kill -9 2 ...
- CloudQuery v1.4.2 发布 | 新增 PolarDB 、数据导入工具
Hello,社区的小伙伴们,CloudQuery v1.4.2 已发布,本次新增数据源 PoalrDB.新增「数据导入」工具等,并且通过我们的努力,终于大幅减少了服务运行所需内存资源~ImageIma ...
- Spring Aop 获取入参和出参
1. 概述 本次,我利用 Spring Aop 的注解方式获取切入点的入参和出参,因为比较简单,所以就直接上代码了. 2. 代码编写 注解类 /** * 日志注解 * * @author Jianta ...
- unity http json前后端通信
1. 配置请求体中的Json字符串 1 request_data = new RequestBodyClass(); 2 request_data.a = "Json第一个字段"; ...
- DELPHI + uniGUI 开发CentOS环境下的Apache模块遇到的问题
一直以来,用uniGUI做开发都比较顺手,但是缺点是这样做的只能在Windows环境下使用.而我们现在租用的虚拟服务器,往往都是CentOS的环境. 而DELPHI + uniGUI能不能做LINUX ...
- B - Beautiful Now HDU - 6351
https://vjudge.net/contest/314399#problem/B Anton has a positive integer nn, however, it quite loo ...
- UPX 脱壳 学习
加壳是什么? 程序保护机制,保护程序不那么容易呗逆向. upx是一种开源的压缩壳软件 命令行执行: 加壳:UPX + sample.exe 脱壳:upx -d sqmlp.exe(一般不管用) 手动脱 ...