1.整数在内存中的存储
正整数的原,补,反码都相同。
负整数的三种表示方法各不相同。
提示:负数的反码等于原码符号位不变,其他位置的二进制位取反。
负数的补码等于反码+1.
对于整型来说:数据存放在内存中其实存放的是补码。
为什么呢?
2.大小端字节序和字节序判断
首先看一个代码(VS系统)
#include <stdio.h>
int main()
{
int a = 0x11223344;
return 0;
}
这个代码的结果呢?
可以看出低位的数字存储在了前面;这是小端存储
2.1什么是大小端?
超过一个字节的数据在内存中存储时,就有存储顺序的问题,按照不同的存储顺序,我们可以把分为大端字节存储和小端字节存储。在不同的编译器中不同。
大端存储:通俗来说就是数字的比较大的位数存到了前面。就像 0x11223344,如果为大端存储的话那在内存里面 先存储的就是 11 22 33 44,和上面的图片相反
小端存储:就是数字的比较小的位数存到了前面。和上图一样!!!
一个自编小口诀,前大大,前小小。就是前面如果是数字位数大的数字的话就是大端存储,反之相同。
2.2为什么有大小端呢?
2.3那怎么知道编译器是什么存储呢?
可以写一个小代码来研究
#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("⼩端\n");
}
else
{
printf("⼤端\n");
}
return 0;
}
这个代码这样看,在最上面的check 函数中先给 int i = 1,之后返回时将 i 的 强制转换为 char * 是因为 本来 存储的是 int 变量 占了4个字节 ,如果把这个 存储的 字节 单独拿出来看呢, 如果 前面 第一个 字节存的 是 01,那就代表了 将小位的数字存在了前面,为小端存储,反之相同。
2.4几个有趣的小练习
#include <stdio.h>
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
输出结果a b = -1,c =255.
这个练习,可以看出用char 来存储数字,但存储数字的时候存的是补码。因为char 其实是signed char的缩写。所以a b 其实是相同的。因为 sighed char 是有符合的字符型,只保存一个字节,所以 3个变量的二进制位都是 11111111.因为前两个变量是有符号的,所以在下面要求打印%d 时发生整型提升,因为两个变量是有符号的,所以提升时前面的符号位为1.所以提升时32个二进制位全为1 。但打印时 是以原码打印,因为前两个变量的符号位是1 是负数。负数的原码 等于 补码减一 并取反。所以最终前两个变量的 原码就是 第一位二进制位和最后一位二进制位为1,其他全为0,所以等于-1.
但不同的是 unsighed char 因为 是无符号整型,在整形提升时,c变量的二进制位前面全部补0.所以c 的二进制表示是 00000000 00000000 00000000 11111111.因为正数的原反补码相同所以,c = 255.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
第一个a 打印出来一个特别大的数字,第二个char
这个练习要求打印 %u 就是无符号整型。给的两个有符号的 char 类型。
第一个 char 因为是负的-128,所以他的 原码其实就是 10000000,对他整型提升后 就是。
11111111 11111111 11111111 10000000,因为要打印无符号整型,所以第一个二进制位不看做符号位。,所以这个数字是正数,原码反码补码相同,所以打印出来一个特别大的数字。
第二个 a 其实写下来和第一个a 的原码其实是一样的,因为 char 只有 一个字节8个二进制位。-128 和 128的二进制位其实相同,所以两个数字的结果相同。
#include <stdio.h>
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
这个代码的最终结果是255.
为什么呢?首先给了一个 1000的i 循环。将a【i】 = -1 - i;
因为a 【i】是char 类型 只有 1个字节 8 个二进制位。第一个a【i】 = -1.二进制位是
10000001.每次都减一直到 11111111 ,它的原码就是 10000000.在vs 中这个数字为-128
所以-128再减一 就变成了 127 在一直减到 00000000.其中总共走了 255次。出现 0 。
strlen 计算字符直到出现 0.所以得出255.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
无符号char型的范围如上图。
本题的无符号char 类型 的范围 0 -255.因为for 循环一直打印到255.但255加一 又 等于 0.
这就会导致死循环。
#include <stdio.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
这题也是一个死循环,因为是无符号整型,所以当减到0时,再减一,就变成了 32个二进制位都是 1.这样一直循环下去。
3.浮点数在内存中的存储
常⻅的浮点数:3.14159、1E10等,浮点数家族包括: float 、 double 、 long double 类型。浮点数表⽰的范围:float.h中定义
3.1练习
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;
}
由上图可以看出浮点数和整型的存储方式是不同的
3.2浮点数的储存
上图则是浮点数在计算机内部的表示方法。
3.2.1 浮点数存的过程
M保证是 1<=M<2.
E如果为8位取值范围就是0-255,如果为11位就是0-2047.根据IEEE的规定,需要给E中额外存入一个数字 8位E就是 127 ,11位就是 1023.⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
3.2.2浮点数取的过程
指数E从内存中取出还要分为三种情况。
1.E不全为0或1.
这时,浮点数就采⽤下⾯的规则表⽰,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1。⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位