亲宝软件园·资讯

展开

C语言数据存储

Zoroxs 人气:0

内存简单介绍

大家肯定经常听说内存这个词,内存到底是什么呢? 在计算机中,进程都要加载进内存中,也是我们各种数据的流通途径,C语言中,大家肯定都知道指针变量,指针变量中保存的就是内存的地址,那么,什么是内存的地址呢?

内存的单位是字节

对于32位的机器,有32根地址线,每根地址线在寻址时,产生的高低电压分别为0/1,那么32根地址线产生的地址就会是

00000000000000000000000000000000

00000000000000000000000000000001

00000000000000000000000000000010

11111111111111111111111111111111

这里就有2^32次方个地址

大家应该知道,还有64位的机器,64根地址线又有多少个地址呢,大家可以计算一下

在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,

所以一个指针变量的大小就应该是4个字节。 那如果在64位机器上,有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

这串编号就是内存单元的地址,就像酒店的房间号一样,对应着内存中的一个字节大小的房间

我们在vs中来看一下

这里是以十六进制的方式展示的,大家也知道,32个数字看起来太长了。

整数与字符在内存中的存储

关于c语言中的数据类型,大家在写了这么多代码后肯定也很清楚了,C语言中有整型、浮点型、字符型、等等

我们来研究一下整数在内存中是如何存储的

大家都知道,定义变量,会在内存中开辟空间来存储

int a = 20;

int类型在vs中占据4个字节的空间,那么如何存储呢?

这就涉及到原码反码补码的概念

原码:

直接将数值按照正负数的形式翻译成二进制就可以得到原码

反码:

将原码的符号位不变,其他位依次按位取反就可以得到反码

补码:

反码+1就得到补码。

对于整型变量来说,内存中存放的其实是补码

使用补码,可以将符号位和数值域统一处理,加法和减法也可以同一处理,因为CPU只有加法器

eg:

int a = 20;
//原码 : 直接写二进制    00000000000000000000000000010100
//反码--补码  -- 正数的原反补相同
int  b =  -5;
//原码:   10000000000000000000000000000101
//反码:符号位不变,按位取反
//        11111111111111111111111111111010
//补码:反码+1
//        11111111111111111111111111111011  --   -5的补码

上边可以看见-5的补码是11111111111111111111111111111011 ,我们如何确认呢?

转换成16进制为fffffffb

大家可以看到确实是使用补码存储的,但是为什么是倒着存储的,后边再来说,这是由于大小端的问题

给大家举几个例子,不知道存储无法分析出结果代码

//1.
#include <stdio.h>
int main()
{
  char a = -128;
  printf("%u\n",a);
  //-128 原码:  10000000 00000000 00000000 10000000
  //-128 反码:  11111111 11111111 11111111 01111111
  //-128 补码:  11111111 11111111 11111111 10000000
  //存在a里面的,因为只有一个字节   10000000
  //所以会当做无符号整数打印 --整形提升
  //提升为  11111111111111111111111110000000
  //所以结果是一个很大的整数,大家可以尝试一下
  return 0;
}

#include <stdio.h>
int main()
{
  char a = 128;
  //128的原码反码补码 : 00000000000000000000000010000000
  //存在a里的:10000000
  //整形提升 :11111111111111111111111110000000
  //所以结果还是那个很大的整数
  printf("%u\n",a);
  return 0;

例子就简单给大家举到这里,大家一定要记住整数在内存中是以补码的形式存储的

字符存储的是ASCII码,所以和整数同

浮点数在内存中的存储

我们来看一下浮点数在内存中的存储

抛砖引玉:

#include <stdio.h>
int main()
{
    int n = 9;
    float *pFloat = (float *)&n;
    printf("n的值为:%d\n",n);
    printf("*pFloat的值为:%f\n",*pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n",n);
    printf("*pFloat的值为:%f\n",*pFloat);
    return 0;
}

上面代码的打印结果到底是什么呢?

是不是非常出乎大家的意料呢,这里就可以看出,浮点数的存储肯定和整数是不同的。那浮点数到底咋存的呢?

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

看得很迷糊,直接上例子

v = 5.5

= 101.1 --二进制表示

= 1.011 * 2 ^2 – 科学记数法表示

因为是正数 s = 0

m = 1.011

e = 2

IEEE 754规定:

对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

IEEE 754对有效数字M和指数E,还有一些特别规定

1=<M <2 ,所以M可以写成1.xxxxx 所以可以舍去1 ,只存储xxxxxx

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字

对于指数E,E是一个无符号整数,但是科学记数法指数是可以出现负数的,所以

IEEE 754规定了偏移量,如果E为8位,则加上127 ,如果E为11位,则加上1023

我们举个例子

float f = 8.5f;
//二进制 : 1001.1
//科学计数法表示: 1.0011*2^3
//S = 0 M = 1.0011  E = 3
//存储进去的应该是:
    0 10000010 00110000000000000000000
//我们可以验证一下
        转换成16进制
 0100 0001 0001 1000 0000 0000 0000 0000
//41 18 00 00

我们来看一下代码

和我们想的一样

我们再来举一个负数的例子

float t = -3.5f;
//二进制: 11.1
//科学记数法: 1.11*2^1
//S = 1 M = 1.11 E = 1
//存储:
1  10000000 11000000000000000000000
//转换成16进制
1100 0000 0011 0000 0000 0000 0000 0000
c0 60 00 00

那我们从内存中读取出来的二进制位如何解析成浮点数呢

关于E,有三种情况

E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1

2.E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,

有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于 0的很小的数字。

3.E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);

我们来分析一下最开始的题目

浮点数和整数的存储就介绍到这里了,有哪里不清楚的朋友可以私信我

大小端存储模式及简介

上边有一个悬念,为什么是倒序存储的

那什么是大端小端呢?

大端(存储)模式: 数据的低位存储在内存的高地址中,数据的高位存储在内存的低地址中

小端(存储)模式: 数据的低位存储在内存的低地址中,数据的高位存储在内存的高地址中

那为什么会有大小端呢?

内存中以字节为单位,但是比如int 是4个字节,那如何安排这个4个字节呢?就导致了大小端存储模式

例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则 为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式 还是小端模式。

那我们如何测试当前电脑是哪种存储模式呢?

int main(void)
{
	int a = 0x11223344;
	return 0;
}

我们调试打开内存看一下

很明显,数据的低位存储在内存低地址中,所以为小端存储模式。

我们能不能写一个程序,直接告诉我们大小端呢?

我们来分析一下

我们来看一下代码

int decide_byte_orde()
{
    int i = 1;
    return *(char *)&i;
}

我们来测试一下

总结

深入理解数据的存储是非常有必要的,我们之后碰到很多问题都会豁然开朗,大家一定要好好研究一下

加载全部内容

相关教程
猜你喜欢
用户评论