非常规入门C语言:2、初始数据类型|世界最新
日常生活中,在我们周围充斥着各种各样的数据。这些数据有着不同的形式,且分布在不同的载体上。
但有些数据是有意义的,如费用余额、学号、身份证号、姓名、年龄等;而有些数据是无意义的,如......
而计算机被发明以来,天生就是服务于科学计算的,即:一切计算机问题都是数学问题。
(资料图)
在上一章中,计算机底层的计算方式是通过二进制来确定的。同时为了能够方便地使数据参与运算,还引出最小处理宽度(宽度就二进制位数)和控制指令。
那我们可以把哪些数据放在计算机“里面”呢?
理论上,所有数据都可以。但首先我们解决数据从哪里来的问题,即信息的数字化。
首先,和数值相关的东西,我们可以很方便存储在计算机中,剩下的问题就是精度和宽度的问题。
其次,我们还需要解决非数值信息的数字化问题。
那么我们先解决哪个问题呢?
为了避免问题的延伸,我们先解决宽度的问题,即给不同的数据分配一个确定的类型,即数据类型。
首先,我们能确定的第一个数据类型就是计算机的最小处理宽度,无论是哪种机器,无论它的最小处理宽度是多大,我们都称之为“字”,字的宽度称之为“字宽”。那么为了和二进制搭上关系,我们采用倍数增长的方式,扩大数据类型的表示范围。
字->双字->四字->八字->16字->32字->64字......
你想扩大多少倍都可以,但必须是前一个数值的2倍。因为这样的处理方式的难度是最低的。
于是,基于“字”概念的数据类型就出现了。但是它本身并不具有通用性和实际意义,因为“无论它的最小处理宽度是多大,我们都称之为‘字’”。
这时,我们应该想到貌似有一个8位的数据。没错,在上一章中,我们使用8位二进制的数据完整表示了控制字符(如换行等)、大小写字母、数字和常用标点符号。那么,干脆就用8位作为最小处理宽度。
那么,给个名字,把8位宽度的称为“字节”,同时将“字节”确定为通用的基本字宽。
这时,我们得到了一些较为原始的数据类型:字节、双字节、四字节......
我们能有多大的数呢。。。这是一个问题。因为需要看CPU能处理多大的数。32位的CPU最大一次性处理32位,64位CPU最大一次性处理64位。为啥是一次性呢?理论上来说,只要你能提供足够的存储空间,数字多大都没问题,但是运算是一部分一部分来的,极端情况也有可能是一位一位来计算的。这种由CPU确定的最大一次性处理宽度,称之为“数据宽度”。
早期计算机的数据类型就是这样。。。很枯燥,很直白,但也很简约。
但这不是我们的目标,我们的目标是“星辰大海”。
1 + 1 = 2
1 - 2 = ?
当你在计算时有没有想到这样一个问题:正整数可以直接表示,但负整数和小数没办法直接在计算机中表示。
因为计算机中没有负号和小数点的概念。解决办法也是相当简单粗暴:用正整数来表示不就好了?
举个例子:
使用字节来控制字宽。那最长就是8位,表示的范围就是0到255(即0000 0000 ~ 1111 1111)。
首先我们先确定一下正数与负数的交界点,那就是0。那我们就可以确定,0加上1就是正数,0减掉1就是负数。
如果我们需要表达-2,那就是0000 0000 - 0000 0010就等于1111 1110,因为0减1不足,向前借2。
那么,教科书里是怎样教的呢?
嬴寒隐约记得,取反码再加1。。其实很简单,根本用不着反码。所谓反码,只是计算机的一种取反运算而已,因为相比加减运算,取反的操作,那简直是太快了,瞬时就可以完成。
为什么是瞬时呢?因为只需要对前面的位进行取反,最低位根本不用管就可以完成一个负数的完整表达。根本不可能使用加减运算。但作为人类,我们有时有必要理解计算机的运算方式,但有时又没有必要。而像这种特别基础,基础到对后面的知识没有什么太大影响的东西,我们只需要理解最人性化的思路就可以了。
但是教科书却用计算机运算的方式来介绍负数的表示原理,无疑是给刚接触的小伙伴带来了沉痛的负担。
各位小伙伴可能已经发现,在计算机中,处于最高位的0是一个非常神奇的存在。我们通过它能够辨别是正数还是负数,也能够通过它,来判断一个数据是不是正确的(就是低位数据的每一位运算的结果,称为校验)。其实我们还可以利用0来确定真假值,比如0为真,非0为假,我们称之为“布尔值”。
刚刚我们找到了负数的表示方案,此时,我们就需要找到非整数的表示方法了。那么如何利用整数来表示非整数呢?
一种方式是直接规定第多少位开始是小数部分,当然这种方式没人会喜欢,而且灵活性也很差。
其实这里我们就考虑到了小数点的变动问题:小数点是不动(小数位数固定)还是变化的(小数位数不定)。显然,两种形式各有优劣。
既然整数的表达最方便,那能否让小数和整数直接挂钩呢?可以。
我们以科学计数法再举个例子:
使用二进制表示259.56,如果将其转换为科学计数法就是2.5956*10^2。
但很显然我们还是没办法直接表示,既然缩小不行,那就尝试放大。于是我们可到等价的值25956*10^-2。这时其中所有相关的数值我们都是在计算机方便的表示:25956、10和-2。
但是不要忘了,计算机是基于二进制的,并非十进制,那就意味着,必须将这仨兄弟都转换成二进制就可以了。
可是,好像不太容易啊。因为我们一直是按照十进制来处理,但换成二进制中,小数点的变动就不一样了。
所以我们将259.56拆成两部分,使用常规方案表示整数部分259,使用小数表示法表示小数部分0.56。
在十进制的世界中,1是1*10^0,10是1*10^1,0.1是1*10^-1。
在二进制中也是基本一致的,1是1*2^0,2是1*2^1。
也就是说,我们需要表达某个大数,就是让这个数除以基数,十进制的基数就是10,那么二进制的基数就是2;反之,如果我们需要表达一个凑不整基数的小数,就需要让这个数乘以基数。
例如:256就是200+50+6=2*100 +5*10+6*1,表达逻辑是让原来的数缩小,让基数增大。
再如:0.25就是0.2+0.05=2*10^-1+5*10^-2,表达逻辑是原来的数变大,让基数减小。(说法不是很严谨,因为不是让基数本身发生变化,而是权值,或者说是指数部分)
那么,0.56表示成二进制就是让这个数一直乘以2,直到小数部分没有值为止。
0.56 * 2 = 1.12 -> 取 1
0.12 * 2 = 0.24 -> 取 0
0.24 * 2 = 0.48 -> 取 0
0.48 * 2 = 0.96 -> 取 0
0.96 * 2 = 1.92 -> 取 1
……
很长很长:.100 0111 1010 1110 0001 0100 0111 1010 1110 0001 0100 0111 1011
所以就涉及到精度问题。。。因为我们不可能把所有精力和空间全浪费在小数的处理上。除非。。。有东西能够帮我们自动完成这件事。
那么,上面的259.56就可以分成三部分了,整数部分、小数部分、小数点位数。
当然,这只是一个示例。并不一定是现代计算机中用到的表达方式。
至此,我们已经利用计算机中原有的东西实现了一些类型,如:数值、字符、布尔值,暂且称他们为“初始数据类型”吧。