对于C语言中数据类型的介绍与理解
对于数据类型的介绍与理解
l 对于“数据类型”的理解
每一个变量都是一块空间,数据以二进制的形式存储在这块空间中,而每个变量所占的字节声明了其所占的内存大小,如int与float类型都占用4个字节的空间,他们的区别在于数据类型不同,数据类型规定了对于内存中数据的解读方式,正因如此,int与float才是两种不同的类型,都有各自存在的意义,同样都是四个字节,从实质上他们所能存储的二进制数据完全相同,但解读方式不同,他们的内容也就不同,所表示的内容也不同。后面的内容将以此理念为基础。
l 对变量、常量、表达式的理解
变量本质上是一块内存空间,定义一个变量就好像是在内存中开辟了一块空间,对于这块空间我们的权限比较大,可以读出数据,也可以写入数据,所以我们称之为变量。根据变量类型的不同,分配的空间的大小也不同,就像宾馆里的房间,有单人间、双人间、豪华间等等。
常量本质上也是一块内存空间,与变量的区别在于,我们对于这块内存空间管理的权限比较小,只能读出数据,而不能写入,常量的内容在被定义时就确定了,中间不能被改变。对于常量不要认为只有define、const定义的才是,常量的范围广着呢,比如int a=1;这里的1就是一个整数型常量,这样的常量也都有一个默认的数据类型,比如这里的1就是int型,如果是1.1的话,默认都是double型(注意不是float),这样理解可以避免很多错误。
变量、常量再加上一些运算符就构成了表达式,所谓运算符,如+-*/,当然C语言中还有更多其他运算符。表达式的本质上也可以看做一个变量,如1+2这样一个表达式,他就好像一个整数型的变量3,在内存中占4个字节,而表达式与一般的变量也有区别,他相当于一个中间变量,也就是他只是临时出现一下,使用完之后瞬间就没了,但是不能小看这一瞬间,就是因为这一瞬间的出现,他就必须受到作为变量应该受到的限制,如不能超越极值,否则就会溢出,举个例子如2018*2018*2018*2018*2018这么一个表达式,他的实际值已经超过了4字节整数型int所能存储的极限值,这时候就会出现问题了,所以说,表达式本质上也是一个变量。对于表达式的理解,不要停留在1+2,3*5等加减乘除的级别,C语言中的表达式类型很多,它的值也不一定的是整数型。
l C语言中的数据类型
C语言中常用的数据类型有以下几个:
char short int long float double
还有一些其他数据类型:
unsigned char、unsigned int等无符号修饰
long long、long double字节更多,适用于某些情况
还有一些偶尔用到的数据类型:
bool(布尔)、wchar_t(宽字符)
l 对于数据类型转换的理解与介绍
个人认为数据类型转换的本质是:内存重新分布。
举个例子,比如float型的1.1转换为int型则为1,而实际上,float与int型都为四个字节,1.1在float型变量中的二进制分布与1在int型中的二进制分布是不同的,在类型转换的过程中就会重新分布内存,使1.1的内存变为1的内存,从而实现类型转换。其他类型转换各不相同,但都遵循此理。
C语言中数据类型转换大体分为两种,一是自动转换,而是强制转换。
在表达式中,当运算符操作的多个变量或者常量的类型不同时,系统将自动进行数据类型转换,比如:表达式1+1.1。1作为整数型常量,当他与double型常量1.1相加时,会被转成1.0的double型,然后再相加,得到2.1。像这样在运算中系统自动进行的类型转换就是自动转换,一般的自动转换遵循的原则就是不损失精度,比如刚才的1+1.1。如果是把1.1转为int型,结果就是2,这样做就损失了精度,所以这样的自动转换是有一定的原则的:低字节转为高字节,极限范围小转为极限范围大。具体的转换也可参考下图:

其中向左的箭头表示必定转换,向上的箭头表示类型不同时转换。
所谓必定转换,就是只要出现在表达式中就转换,即使是两个同类型相加也会转换。
l 关于补码的知识
十进制数有正负之分,如1和-1,但所有数据本质上都是以二进制存储在内存中的,对于负数,也有专门的储存方法,比如我们要在int中存一个-1,那么先把1的二进制表示出来就是:00000000000000000000000000000001,我们称这种直接把十进制数用二进制表示出来的称作原码,如果表示负数,我们要把第一位改为1,就是:10000000000000000000000000000001(不懂的话下面关于有符号与无符号有详细解释),这个二进制称为反码,但实际上在内存中-1的表示并不是10000000000000000000000000000001,而是11111111111111111111111111111110,先说一下这个是怎么得到的,第一位的1不变,后面的每一位都取反(就是0换成1,1换成0),就能得到这样一组二进制数据,这样经过原码变反码,反码再变化得到的就是补码,实际的-1在int的32位(4个字节)中存储方式也就是这样。
关于为什么在计算机中不使用反码而是用补码来表示负数,这里有一个好处就是:做运算的时候,补码更方便。比如:要计算1+(-1),如果使用反码表示的-1,程序在运算的时候就要先判断数字是正还是负,如果是负还要进行转换,然后才能相加,而如果负数使用补码表示的,就可以直接进行运算,不论是正数还是负数运算,都能得到正确答案,这一点可以自己验证。
l 关于内存分布与极值
有符号与无符号(unsigned)的内存分布与极值
以char型为例。char占一个字节,八个位,默认为有符号类型。char类型是字符型,计算机表示字符的方式是通过ASCII码表对照着用数字代表字符,所以char型内存中的二进制数据所对应的十进制数在ASCII码表中对应的字符即为char型字符。
有符号类型的时候,char的八个位中第一个位是符号位(0代表正,1代表负),后七个位是数据位表示实际的内容。所以01111111(2)即为有符号char型的最大值,对应的十进制是127;对于有符号char型的最小值,肯定是一个负数,根据补码的知识,他的最小值是-128。
而对于无符号的char,八个位都为数据位,没有符号位,所以他的最大值就是11111111(2),对应的十进制是255,最小值就是0。
同理,short、int、long型的有无符号的区别于char相同,区别在于他们所占的字节可能不同。
浮点型数据的内存分布
上面说了有符号数与无符号数在内存中的分布,从中可以看出同样的内存分布,其所表达的含义实际上是不同的,原因在于解释方式的不同,按照有符号解释与无符号解释有可能是两种含义,浮点型也有他自己的存储方式。下面以float型为例。
对于浮点型数据,他在计算机中存储是按照二进制的科学计数法存储的,下面举个例子,比如1.25,十进制的1.25转为二进制就是1.01(转换方法不会的看进制转换文章),用二进制表示就是1.01*2^0,当然这个例子比较特殊,如果是11.101的话,我们需要写成1.1101*2^1;如果是0.11101,也需要将小数点后移为1.1101*2^(-1)。对于1.25对应的1.01*2^0,01就是尾数,0就是指数,为正说明符号位是0,这里有一点需要切记,就是虽然这里的指数为0,但实际上指数是有可能为负数的,指数位一共占8个位,明显它里面既要存储有正数也要有负数,这时候我们可能就考虑到用补码的方法来表示负数,跟上面的有符号类型一样,但实际上这里表示负数的方法与上面所提到的有符号是不同的,这里他将所有的数表示为原数字加127取二进制,相当于做了一个整体偏移,比如0就是0+127取二进制,这样的话负数也可以存储在指数位,这样存储有一个好处就是便于比较大小,因为对于浮点数,在运算的时候实际上并不像整数那样直接运算,而是经过一定处理,本身就是很麻烦的,即使使用补码的方式也不能简化其运算,而使用这种方法虽然不能简化运算,却可以简化比较大小,补码在比较大小上就很麻烦了,所以这里并未使用补码的方式来存储指数位中的负数,具体在内存中的存储方法看下面。
float在内存中占4个字节,32个位,在内存中他是这么规定的:
1位符号位、8位指数位、23位尾数位
按照上面的例子1.25的二进制科学计数法表示为:1.01*2^0,01,最高位的1是不变的,所以不需要存储,这里的符号位为0,指数位就是0+127的二进制,即1111111,尾数位就是小数点后面的部分01,所以1.25在内存中存储为:
0 01111111 00000000000000000000001 共计32位。
上面就是我对数据类型的认识。

浙公网安备 33010602011771号