c语言基本类型字节,C语言基本类型与其数据存储方式

好久没有更新博客了,最近对逆向十分着迷,信息安全的知识量是真的庞大,是时候该做一波笔记了,哈哈。

看下图,C语言数据类型分为右边四大类型,这篇博客重点讲基本类型,因为其他类型还没学呢~~

c7b5be21098637195265a4c16d4c8ee4.png

整数类型

数据类型分为 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],0FFh

89: 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],78h

89: 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],0FFh

90: //无符号

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); // -1

printf("%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],8

89: 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的存储方式如下图所示:

d1a1060a98bd1e1bd0d7221f43e19260.png

double的存储方式如下图所示:

51f84993055217f50cf99f32b3b616bb.png

看不懂上面没关系,一步步来,我们拿float来讲解存储,double同理即可。

首先,我们需要搞清出下面两个问题:

(1) 十进制整数如何转化为二进制数

算法其实只要一直除以2,然后取余数即可。

d18d3d5fa6bd161ebaff7a63cc0da3e2.png

(2)十进制小数如何转化为二进制数

算法是一直乘以2,然后取整数部分即可。

3ab1b8163c505304f2da468dba3fda60.png

挺简单的吧,但是如果是十进制小数 0.8 呢?你会发现出现了死循环.....看下图

099e24fcb585b18f35cc47c41ab5ce5a.png

那么很显然,小数的二进制表示有时是不可能精确的

好了,弄懂了上面两个问题,下面来讲讲将一个float型转化为内存存储格式的步骤:

先将这个实数的绝对值化为二进制格式

将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边

从小数点右边第一位开始数出二十三位数字放入第22到第0位

如果实数是正的,则在第31位放入“0”,否则放入“1”

移动n位后的指数部分加上127(中间值,255/2),转化为二进制填入30~23位

正实数转化

拿 浮点数8.25 存储 来举例说明

第一步 8.25绝对值转化为二进制,看懂了上面的转换那就很简单了,那么:

8.25用二进制表示可表示为 1000.01

第二步 简单的说就是将其二进制使用科学记数法来表示,左移一位乘2,右移一位除2,那么

f531957771d6333cf35f52bf1ce4bca2.png

第三步 将[00001000000000000000000] 放入第22到0位,结果如下图:

9044299bb73c5b257618e591ae06dec5.png

第四步 很明显,31位应该放入 0,结果如下图:

ef78f18d602d72dc51115ea7722719eb.png

第五步 其指数部分为3,127+3=130,将其转化为二进制:100000010,填入30~23位,如下所示

0850205ab8ee5f3fe3350eb1ea7b3e86.png

好了,正实数转化为二进制就到此就结束了,那么我们将其转化为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

第二步 科学记数法表示,右移两位

c2b531874453a70d8ba702ce22a28968.png

第三步 这个很明显存的都是0,第四步 由于是正数,所以存的也是 0

e7a01def35fb1d4f413b880938768e37.png

第五步 其指数为-2,127+(-2)=125 ,其二进制为:11111101,将其放入30到23位,结果如下:

6a0626f77f3c9a7cf3c4618bd713f644.png

将其转化为十六进制进行验证,其转化十六进制结果为: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...

第二步 正好不用移,用科学记数法表示为:

327bcd6bc57176683ad9009b8a6c79fb.png

第三步 从小数点后开始取23位放入尾数部分

adb1516e7f2510381ac4b96b05ef62b2.png

第四步 负数,符号位放入 1

d69a85c46cc562d436631aff3f186c59.png

第五步 其指数部分为0,所以30~23位就是127的二进制:01111111

6bbafa87f6a969cef652cf338fa0c2fb.png

将其转化为十六进制

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

嘿嘿,结果正确~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值