【数据在内存中的存储】

目录

  1. 整数在内存中的存储
  2. 大小端字节序和字节序判断
  3. 浮点数在内存中的存储

1. 整数在内存中的存储

整数的二进制表示方法有三种: 原码、反码和补码
三种表示方法均有符号位和数值位两部分,符号位0表示“正”,1表示"负",而数值位最高的一位被当做符号位,剩余的都是数值位

正数的原、反、补码都相同
负数的三种都不同:
原码: 直接将数值按照正负数的形式翻译成二进制就是原码
反码:原码的符号位不变,其他位依次按位取反就可以得到反码
补码: 反码+1得到补码

对于整形来说,数据在内存中存放的是补码,原因在于补码可以吧符号位和数值域统一处理,加法和减法也可以作为加法统一处理。补码与原码互换,运算过程相同,不需要额外的硬件电路

1.1 取值范围

signed char 和 unigned char

以下列出char类型二进制所有的取值,探讨它的范围

【数据在内存中的存储】_第1张图片
对于有符号的char,最大值就是0111 1111,再加1就是1000 0000,如下图

【数据在内存中的存储】_第2张图片
从0到127,再加1就成为最小值-128,全为1时是负数最大值-1,每次递增1的情况是这样

【数据在内存中的存储】_第3张图片

对于无符号的char,127再加1是128,如下图

【数据在内存中的存储】_第4张图片

它的递增图形如下图

【数据在内存中的存储】_第5张图片

2. 大小端字节序和字节序判断

了解了整数存储后,看一个细节

int a = 0x11223344 ;

调试看到该数是倒着存储的

在这里插入图片描述

2.1 什么是大小端

超过一个字节的数据在内存中存储的时候,就有存储顺序的问题了,按不同的顺序,分为大小端存储

大端存储模式: 数据的低位字节存在高地址处,高位字节存在低地址处
小端存储模式: 数据的低位字节存在低地址处,高位字节存在高地址处

2.2 为什么有大小端之分

计算机以字节为单位,每个地址单元对应一个字节,一个字节8bit位,c语言除了8bit的char外,还有16bit的short,32位的long,另外,对于位数大于8位的处理器,如16位处理器和32位处理器,由于寄存器宽度大于一个字节,必然存在着一个如何将多个字节安排的问题,因此导致了两种存储模式

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

2.3 练习

2.3.1 练习1

简述大端字节序和小端字节序的概念,设计一个小程序判断当前机器的字节序 (10分)-百度笔试题

思路: 大端小端在于变量值的存储顺序不同,可以以1值举例,大端会将1存在高地址处,所以可以取出变量一个字节判断,变量地址是低地址处,为0就是大端

也可以利用联合体,给int类型赋值,联合体加一个char类型,取出char的值判断

代码:

//1
int a = 1;
if (*(char*)&a = 0) {
	printf("大端");
}
else {
	printf("小端");
}

//2
union {
	int i;
	char c;
}un;

un.i = 1;
printf("%d",un.c);

2.3.2 练习2

char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);

输出:

【数据在内存中的存储】_第6张图片

a和b都是有符号,存-1打印出的就是-1,c是无符号的,存到char里8个1都是数值,原码值就是最大值255

2.3.3 练习3

char a = -128 ;
printf ( “%u\n”, a) ;
char a = 128 ;
printf ( “%u\n”, a) ;

输出:

【数据在内存中的存储】_第7张图片

第一个char a存的是1000 0000,将它当做无符号打印,整形提升补符号位,补24个1,当做无符号整型打印,所以这个值就是很大的数

【数据在内存中的存储】_第8张图片
第二个,128的二进制是1000 0000,a存的是这个,因为a是有符号的,即使以无符号打印,整形提升依然补充符号位,所以和上面的结果一样

2.3.4 练习4

char a[1000] ;
int i ;
for ( i = 0; i < 1000; i++ )
{
a[i] = -1 -i ;
}
printf (“%d”, strlen (a) ) ;
}

第一次,i=0时,保存-1.i=1时,保存-2。因为数组是char类型,最多到负数最小值-128,根据有符号char的递增图,-128再减1得到127,当减到0时,strlen结束,所以长度就是128 + 127 = 255

2.3.5 练习5

unsigned char i = 0 ;
unsigned int i1;
for ( i = 0; i <= 255; i++ ) 
{
   printf ( "hello world\n" ) ;
}

for ( i1 = 9; i1 >= 0; i-- ) 
{
   printf ( "%u\n", i ) ;
}

上述两个都是死循环,第一个无符号char的取值范围是0-255,所以<=255恒成立,第二个无符号数不可能小于0,所以>=0恒成立

2.3.6 练习6

int a[4] = { 1, 2, 3, 4 } ;
int* ptr1 = (int *)(&a + 1) ;
int* ptr2 = (int *)((int)a + 1) ;
printf("%x,%x", ptr1[-1], *ptr2) ;

【数据在内存中的存储】_第9张图片

假设a的地址是19ff20
ptr1取的是整个数组的地址,+1是跳过整个数组.是图中所示19ff30,,转化为int指针,访问-1就是往后减一个4字节,19ff2c,取整形内容,就是4
将a转换为整形,+1就是原地址加一个字节,图中所示19ff21,转换为int指针,访问值取四个字节,就是,19ff21-19ff25,小端存储由高到低排列十六进制就是00020000,

输出:

【数据在内存中的存储】_第10张图片

3. 浮点数在内存中的存储

先思考一下下面代码的输出结果:

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

输出:

【数据在内存中的存储】_第11张图片

3.2 浮点数的存储

上面的num和*pFloat存的都是一个数,为什么读取的结果差距这么大,这说明整形和浮点数的存储和读取方式是不一样的

3.2.1 浮点数存的过程

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

V = (-1)S * M * 2E
(-1)S表示符号位,当S=0时,V为正数,S=1,V为负数
M表示有效数字,M是大于等于1,小于2的
2E表示指数位

举例5.5,用二进制表示为101.1

【数据在内存中的存储】_第12张图片
相当于1.011 × 22
按照上面的V格式,S=0,M=1.11,E=2

IEEE 754规定:
对于32位浮点数,最高的1位存储符号位S,接着的8为存储指数位E,剩下的23位存储有效数字M
对于64位浮点数,最高的1位存储符号位S,接着的11为存储指数位E,剩下的52位存储有效数字M

【数据在内存中的存储】_第13张图片
因为,1 计算机存储浮点数时,默认M第一位总是1,因此1可以舍去,只存1小数点后面的数字,读取的时候再补上1和小数点,就可以多存一位数字

至于指数E,比较复杂
规定E为一个无符号整数 (unsigned int)
这意味着,如果E为8位,取值为0-255,E为11位,取值为0-2047。但是,科学计数法中的E可能是负数,所以E必须加一个中间值,8位中间数就是127,11位就是1023,比如E为10,保存时加上127,就是137

所以,存的二进制就是,符号位S:0,E指数位127+2=129,M寸011,后面补0

【数据在内存中的存储】_第14张图片
小尾排列就是00 00 B0 40

3.2.2 浮点数取的过程

分为三种情况:

E不全为0或不全为1

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

比如上面的5.5,指数位E转换为十进制是129,减去127就是2,M补上1.,就是1.011,乘2的2次方,就是101.1,转换为十进制就是5.5

E全为0

这时,浮点数的指数E等于1-127(或者1-1023,即为真实值),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数,这样做表示±0,以及接近于0的很小的数字

E全为1

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

3.3 题目解析

现在来看上面的题目

【数据在内存中的存储】_第15张图片
首先第一个,整数n存为9,打印的仍是9,将存的9以浮点数取出,就是

00000000 00000000 00000000 0000009,指数位全为0,就是一个接近于0的很小的数字,就是,所以值为0

将浮点数9.0按浮点数的方式存下来

0 1000 0010 00100000 00000000 0000000
0100 0001 0001 0000 0000 0000 0000 0000 //四位一排

按整数取出来,数据位就是0后面的部分,浮点数的形式取出依旧是浮点数的9.0

后面的整数值如下:

【数据在内存中的存储】_第16张图片
输出:

【数据在内存中的存储】_第17张图片

你可能感兴趣的:(开发语言,c语言,算法)