一、C语言数据类型
1.引入C语言数据类型
- 汇编中的数据类型有byte、word、dword等
- 那么C语言的数据类型除了有一个作用域的概念,其他的都和汇编的数据类型相似
2.学习数据类型的三要素
- 存储数据的宽度:每种数据类型能容纳多大的数据
- 存储数据的格式:虽然底层都是用0、1存储,但是使用不同数据类型时要注意里面存的数据到底是什么样的
- 作用范围(作用域):哪里可以用、哪里不能用什么数据类型
3.C语言中的数据类型
-
如下图所示:
二、整数类型
-
整数类型:char、short、int、long
char 8bit 1字节 short 16bit 2字节 int 32bit 4字节 long 32bit 4字节 -
那么和汇编的byte,word,dword有什么关系呢?我们举一个简单的例子来看一下
void Func(){ char cTemp = 0xFF; short sTemp = 0xFF; int iTemp = 0xFF; } void main(int argc, char* argv[]) { Func(); }
-
所以从数据宽度来说:
- 如果一个数据使用char类型变量存储,翻译成汇编则会用byte数据宽度内存来存储
- 如果一个数据使用short类型变量存储,翻译成汇编则会用word数据宽度内存来存储
- 如果一个数据使用int类型变量存储,翻译成汇编则会用dword数据宽度内存来存储
-
如果存入数据的数据宽度大于数据类型宽度,还是和汇编一样,从低位开始向高位存储,存储规定的数据宽度,多余的高位数据舍弃
void Func(){ char cTemp = 0x12345678; //byte short sTemp = 0x12345678; //word int iTemp = 0x12345678; //dword } void main(int argc, char* argv[]) { Func(); }
-
整数类型分为有符号(signed)和无符号(unsigned)两种:
-
但是在内存中存储的方式完全一样:计算机不管数据有无符号,它就只把数据化成二进制按照指定的数据宽度存在内存中
-
只是在做运算的时候需要注意有无符号的区别
void Func(){ char x = 0xFF; unsigned char y = 0xFF; printf("%d",x); printf("%d",y); }
-
还有在比较的数据大小时,也要注意有无符号的区别:
-
如果一个有符号和一个无符号数比较,那么最终汇编语言会使用有符号数比较的跳转指令JBE、JG等
void Func(){ char x = 0xFF; unsigned char y = 0xFF; if(x>y){ printf("x>y"); } }
-
如果两个有符号数比较,最终汇编语言会使用比较有符号数的跳转指令
void Func(){ char x = 0xFF; char y = 0xFF; if(x>y){ printf("x>y"); } }
-
如果两个无符号数比较,最终汇编语言会使用比较无符号数的跳转指令
void Func(){ unsigned int x = 0xFFFF; unsigned int y = 0xFF; if(x>y){ printf("x>y"); } }
-
-
三、浮点类型
1.浮点类型
- float和double
- float数据类型宽度为32位;double数据类型宽度为64位
2.小数在内存中存储方式
-
十进制、十六进制等整数在可以转化成对应的二进制最终存入内存,但是小数无法像整数那样对样转化成二进制数,但是小数如果想存入内存,它也要想办法转成二进制,那么小数是如何存储的呢?
-
float和double在存储方式上都是遵从IEEE的规范的
-
float的存储方式:
-
double的存储方式:
-
-
尾数部分宽度越长,表示小数的精确度越高,因为有些小数部分表示为二进制数是无限循环的,比如1.3。0.3用二进制表示即不断的乘2:每次乘完2取整数部分作为二进制数,小数部分如果为0,则结束,如果不为0,则取出小数部分继续乘2。那么1.3表示为二进制为1.0100110 0100110 …无限循环下去,如果使用float存储,那么尾数部分精确到了23位;如果使用double存储,那么尾数部分就精确到了52位
3.将float类型转换为内存存储格式
-
先将这个实数的绝对值化为二进制格式(不管正负,转换分为小数部分和整数部分)
-
将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字(1)的右边
-
从小数点右边第一位开始数出二十三位数字放入第22到第0位(不够的用0补)
-
如果实数是正的,则在第31位放入“0”,否则放入“1”
-
如果n是左移得到的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”
-
如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位
如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位
-
接着将得到的32位每4位一组,化成十六进制方便我们表示
4.举例说明
-
8.25转换成浮点数存储:
-
整数部分8转换成二进制为:
1000
8/2 0 ↑ //不断的除以2,取余数 4/2 0 | 2/2 0 | 1/2 1 |
-
小数部分0.25转换成二进制为:
10
0.25*2 0 | //不断的乘2,取整数部分 0.5*2 1 ↓ //直到小数部分为0结束
-
所以最终8.25用二进制表示为1000.10
-
-
小数点左移三位,小数点移到第一个有效数字的右边,接着用科学计数法表示:
- 因为十进制数100.1可以表示为1.001*102。所以二进制1000.10移动后可以表示为:1.00010 * 23
-
从小数点右边第一位开始数出二十三位数字放入第22到第0位
-
因为小数点右边开始为00010,所以将00010后面再补0,一共补充到总长度为22位即可
-
-
因为8.25是正数,则在第31位放入“0”
-
因为小数点当时是左移3位得到的1.00010 * 23,则在指数部分的首位即30位放入“1”
-
因为小数点是左移3位得到的1.00010 * 23,所以3-1=2化成二进制为10,接着在左边补0,补充到总长度为7即可
-
化成十六进制为:0x41080000
0100 0001 0000 1000 0000 0000 0000 0000 0x41080000
四、字符的存储
1.英文字符的存储
-
可以这样存:
char x = 'A'
;也可以这样存:char y = 65
-
ASCII编码:
-
ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符
- 标准 ASCII 码使用7 位二进制数来表示所有的大写和小写字母,数字 0 到 9、标点符号,以及在美式英语中使用的特殊控制字符(最高位,即第八位永远都是0)
- 扩展 ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号
-
常用的字符对应的ASCII码为:
二进制 十进制 十六进制 字符 0011 0000 48 30 0 0011 0001 49 31 1 0011 0010 50 32 2 0011 0011 51 33 3 0011 0100 52 34 4 0011 0101 53 35 5 0011 0110 54 36 6 0011 0111 55 37 7 0011 1000 56 38 8 0011 1001 57 39 9 0100 0001 65 41 A 0100 0010 66 42 B 0100 0011 67 43 C 0100 0100 68 44 D 0100 0101 69 45 E 0100 0110 70 46 F 0100 0111 71 47 G 0100 1000 72 48 H 0100 1001 73 49 I 0100 1010 74 4A J 0100 1011 75 4B K 0100 1100 76 4C L 0100 1101 77 4D M 0100 1110 78 4E N 0100 1111 79 4F O 0101 0000 80 50 P 0101 0001 81 51 Q 0101 0010 82 52 R 0101 0011 83 53 S 0101 0100 84 54 T 0101 0101 85 55 U 0101 0110 86 56 V 0101 0111 87 57 W 0101 1000 88 58 X 0101 1001 89 59 Y 0101 1010 90 5A Z 0110 0001 97 61 a 0110 0010 98 62 b 0110 0011 99 63 c 0110 0100 100 64 d 0110 0101 101 65 e 0110 0110 102 66 f 0110 0111 103 67 g 0110 1000 104 68 h 0110 1001 105 69 i 0110 1010 106 6A j 0110 1011 107 6B k 0110 1100 108 6C l 0110 1101 109 6D m 0110 1110 110 6E n 0110 1111 111 6F o 0111 0000 112 70 p 0111 0001 113 71 q 0111 0010 114 72 r 0111 0011 115 73 s 0111 0100 116 74 t 0111 0101 117 75 u 0111 0110 118 76 v 0111 0111 119 77 w 0111 1000 120 78 x 0111 1001 121 79 y 0111 1010 122 7A z
-
2.中文字符的存储
-
GB2312编码规则:
- 天朝专家把那些127号之后的奇异符号们(即EASCII)取消掉,规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,还把数学符号、罗马希腊的 字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了
-
**所以截取一个字符串的一段,如何区分是英文还是中文?**就看这个字节数值的范围,如果小于128就表示英文;如果大于则是汉字的两字节截取了一半
-
GB2312表:可以看到如果最终计算机存储的是0xB0A1,那么使用GB编码规则,这两个字节的值就表示
啊
这个汉字。但是注意:是计算机中存储的值为0xB0A1,而计算机内存中是按字节为单位倒着存的,强调过很多次了,所以我们实际上用啊
的时候要写成0xA1B0
五、作业
-
将float类型的12.5转换成16进制
-
将12.5用二进制表示:
整数部分: 12 化成二进制--> 1100 小数部分: 0.5 化成二进制--> 1 因为0.5*2=1.0 1 综上12.5化成二进制为1100.1
-
小数点左移到第一个有效位的右边,此时用科学计数法表示为:1.1001*23。故左移了3位
-
将此时的小数位从21位依次存到0位,如果不够则补0
-
因为12.5是正数,所以31位为0
-
又因为小数部分是左移,所以30位为1
-
且因为小数二进制时左移3位得到的,所以3-1=2化成二进制为10,将10存入指数部分,不够的左边补0
-
最后每四位一组化成十六进制为
0100 0001 0100 1000 0000 0000 0000 0000 0x41480000
-
综上12.5转成十六进制为:0x41480000,存储在内存中为(倒着存入)00 00 48 41
-
验证一下:
-