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型转化为内存存储格式的步骤:

  1. 先将这个实数的绝对值化为二进制格式
  2. 将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边
  3. 从小数点右边第一位开始数出二十三位数字放入第22到第0位
  4. 如果实数是正的,则在第31位放入“0”,否则放入“1”
  5. 如果n 是左移得到的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”
  6. 将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
嘿嘿,结果正确~


原创粉丝点击