C语言基本类型与其数据存储方式
来源:互联网 发布:马克飞象 mac客户端 编辑:程序博客网 时间:2024/06/08 06:12
好久没有更新博客了,最近对逆向十分着迷,信息安全的知识量是真的庞大,是时候该做一波笔记了,哈哈。
看下图,C语言数据类型分为右边四大类型,这篇博客重点讲基本类型,因为其他类型还没学呢~~
整数类型
数据类型分为 char short int long 四种
char 8BIT 1字节 -----宽度-----> byte
short 16BIT 2字节 -----宽度-----> word
int 32BIT 4字节 -----宽度-----> dword
long 32BIT 4字节 ---------------------------
在很多年前的16位计算机上,int类型是占2个字节的,到了32位计算机上,int类型变成了4字节,然而long没变,原来4字节,现在还是4字节,这是个历史遗留问题,平时我们使用前三个即可。
88: char a = 0xFF;0040D608 C6 45 FC FF mov byte ptr [ebp-4],0FFh89: short b = 0xFF;0040D60C 66 C7 45 F8 FF 00 mov word ptr [ebp-8],offset main+20h (0040d610)90: int c = 0xFF;0040D612 C7 45 F4 FF 00 00 00 mov dword ptr [ebp-0Ch],0FFh观察88行代码以及对应下一行汇编代码,可以发现,当数据类型定义为char类型时,将0xFF放入了一块byte大小的内存(栈)中。
同理,short和int则放入对应的word大小和dword大小的内存空间中。
如果从数据宽度的角度讲,C语音中的char就是汇编中的byte,short就是汇编中的word,int就是汇编中的dword。
再观察以下代码,注意宽度
88: char a = 0x12345678;0040D608 C6 45 FC 78 mov byte ptr [ebp-4],78h89: short b = 0x12345678;0040D60C 66 C7 45 F8 78 56 mov word ptr [ebp-8],offset main+20h (0040d610)90: int c = 0x12345678;0040D612 C7 45 F4 78 56 34 12 mov dword ptr [ebp-0Ch],12345678h可以发现,0x12345678(32位)可以存到byte里?word里?
答案其实很明显,当然不可以。
调试程序,观察内存,可以得出以下结果:
[ebp-4] 这内存地址,大小byte,存入的结果为 78
[ebp-8] 这内存地址,大小word,存入的结果为 78 56
[ebp-0xC] 这内存地址,大小dword,存入的结果为 78 56 34 12
Q:为什么存入内存(栈)中时,是按照4字节4字节的存呢(-4,-8,-0xC....),而不是按照实际大小存,这样不是会造成空间浪费吗?
首先因为它是用堆栈存的,确实是有空间浪费的,但是这样的寻址效率比挨着存会高很多。
其实整数类型分为有符号(signed)和无符号(unsigned)两种的,上面默认省略的就是有符号的
那么有符号和无符号有什么区别呢?这个问题在学习汇编的时候就十分纠结,那么下面我们来观察下有符号和无符号在内存中是如何存的?
88: //默认就是有符号89: char a = 0xFF;0040D608 C6 45 FC FF mov byte ptr [ebp-4],0FFh90: //无符号91: unsigned char b = 0xFF;0040D60C C6 45 F8 FF mov byte ptr [ebp-8],0FFh观察汇编代码,调试观察内存其实可以知道,不管是有符号还是无符号,在内存中是没有任何区别的,你让我存什么,我就存什么。
其实所谓的有符号与无符号,计算机本身是无辜的,他只是存了一堆数而已。
那我们使用printf函数打印出a和b
//默认就是有符号char a = 0xFF;//无符号unsigned char b = 0xFF;printf("%d\n",a); // -1printf("%d\n",b); // 255计算机内存中存了相同的0xFF,为什么打印出来的结果却不同呢?
其实如果你把0xFF当有符号来看,那么就是-1,如果当无符号来说,那么就是255。所以有符号和无符号,其实就是用的这个人(程序员)说了算,同样是0xFF,有符号就是-1,无符号就是255。
一般有符号与无符号,在大小比较,类型转换,数学运算这几种情况我们需要特别注意。
有符号与无符号,在内存中存储的方式完全一样,只不过在用的时候,你把它当成有符号用,那就是有符号,反之也是一样。计算机是不管有符号还是无符号,当你是有符号时,他就会生成对应的有符号对应的汇编指令,无符号就会生成无符号对应的汇编指令。
浮点类型
浮点类型通俗点的说就是拿来存小数的,有float和double两种。
上面的整数类型是会直接转化为二进制形式存进去,那么浮点型在内存中是这么样子的呢?
88: int a = 8;00401298 C7 45 FC 08 00 00 00 mov dword ptr [ebp-4],889: float b = 8.25;0040129F C7 45 F8 00 00 04 41 mov dword ptr [ebp-8],41040000h可以发现int类型时,其值是0x8,而float类型时,却是0x41040000,这个数是这么来的呢?
任何一个整数最后都是可以转化为二进制的,那么小数呢?这个恐怕困难了,下面我们就来讲讲浮点型的存储方式。
首先,float和double在存储方式上都是遵从IEEE的规范的
float的存储方式如下图所示:
double的存储方式如下图所示:
看不懂上面没关系,一步步来,我们拿float来讲解存储,double同理即可。
首先,我们需要搞清出下面两个问题:
(1) 十进制整数如何转化为二进制数
算法其实只要一直除以2,然后取余数即可。
(2)十进制小数如何转化为二进制数
算法是一直乘以2,然后取整数部分即可。
挺简单的吧,但是如果是十进制小数 0.8 呢?你会发现出现了死循环.....看下图
那么很显然,小数的二进制表示有时是不可能精确的
好了,弄懂了上面两个问题,下面来讲讲将一个float型转化为内存存储格式的步骤:
- 先将这个实数的绝对值化为二进制格式
- 将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边
- 从小数点右边第一位开始数出二十三位数字放入第22到第0位
- 如果实数是正的,则在第31位放入“0”,否则放入“1”
- 如果n 是左移得到的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”
- 将n减去1后化为二进制,不足七位则在左边加“0”补足七位,超7位则取后七位,放入第29到第23位。
正实数转化
拿 浮点数8.25 存储 来举例说明
第一步 8.25绝对值转化为二进制,看懂了上面的转换那就很简单了,那么:
8.25用二进制表示可表示为 1000.01
第二步 简单的说就是将其二进制使用科学记数法来表示,左移一位乘2,右移一位除2,那么
第三步 将[00001000000000000000000] 放入第22到0位,结果如下图:
第四步 很明显,31位应该放入 0,结果如下图:
第五步 这个也比较明显,左移,指数为3,那么第30位存的是 1,结果如下图:
第六步 将指数(3) - 1 = 2 ,转化为二进制就是 10,左边加0补齐7位(上一步占了一位,所以7位),结果就是 0000010
好了,正实数转化为二进制就到此就结束了,那么我们将其转化为16进制结果验证一下。
将其4位4位分割,最终转化为十六进制值是:0x41040000
89: float b = 8.25;00401298 C7 45 FC 00 00 04 41 mov dword ptr [ebp-4],41040000h没错,看汇编代码中也是0x41040000,说明我们转化为二进制是正确滴。
再举一个例子,浮点数0.25是如何存储的呢?(若是看懂了上面的例子,那么这个也就容易理解了)
第一步 转化为二进制后结果:
0.25用二进制表示可表示为 0.01
第二步 科学记数法表示,右移两位
第三步 这个很明显存的都是0,第四步 由于是正数,所以存的也是 0,第五步 由于是右移,所以存的也是 0。结果如下图
第六步 指数(-2) - 1 = -3,-3的二进制是如何表示呢?看如下代码
十进制 十六进制 二进制-1 0xFF 11111111-2 0xFE 11111110-3 0xFD 11111101取其后七位(1111101)放入29到23位,结果如下:
将其转化为十六进制进行验证,其转化十六进制结果为:0x3E800000
89: float b = 0.25;00401298 C7 45 FC 00 00 80 3E mov dword ptr [ebp-4],3E800000h比较其汇编结果,可以发现其转换是正确滴
负实数转化
就拿 -1.8 来举例吧,这个有点难受~,数字有点长呀
第一步 将其绝对值转化为二进制,由上面可知,0.8转化二进制是没有精确值的哦
1.8用二进制可表示为 1.11001100110011001100110011001100...
第二步 正好不用移,用科学记数法表示为:
第三步 从小数点后开始取23位放入尾数部分
第四步 负数,符号位放入 1
第五步 由于n(n是科学记数法表示时需要左移或右移的位数,根据第二步可得无移动) 等于0,所以第30位是0
最后一步 0(指数) - 1 = -1,转化为二进制为 11111111,取其后七位
将其转化为十六进制
1011 1111 1110 0110 0110 0110 0110 0110 B F E 6 6 6 6 6其二进制转化为十六进制为:0xBFE66666使用VC++ 6.0运行调试,观察其汇编代码验证
89: float b = -1.8;00401298 C7 45 FC 66 66 E6 BF mov dword ptr [ebp-4],0BFE66666h嘿嘿,结果正确~
- C语言基本类型与其数据存储方式
- c语言数据存储类型
- C语言各种类型变量存储方式
- C语言 数据存储类型分析
- C语言笔记之数据存储类型
- C语言存储类型
- C语言存储类型
- C语言存储类型
- C语言存储类型
- c语言存储类型
- C语言存储类型
- C语言之变量类型和存储方式
- C语言之变量类型和存储方式
- c语言 基本类型
- C语言基本类型
- C中基本的数据类型和数据的存储方式
- 基本类型与其封装类型
- 基本类型与其封装类型
- 欢迎使用CSDN-markdown编辑器
- hibernate框架
- synchronized, reentrantlock,condition
- qt debug输出
- 子域名枚举工具Sublist3r
- C语言基本类型与其数据存储方式
- mysql 数据量大,使用月分区,加快速度大数据查询
- python使用中文
- Pandas秘籍【第四章】
- PE 文件结构分析
- eclipse,debug执行到this调用方法时,就会出现无响应的情况
- INNER JOIN,LEFT JOIN,RIGHT JOIN的连接方式
- mysqlbinlog 查看
- 冒泡排序