目录
一、整数的存储
1.1原码,反码,补码
- 内存中存储的是补码
- 打印的是原码
- 正整数的原码反码补码相同
- 负整数的源码反码补码不同
- 源码的第一位为符号位(0为正,1为负),反码为源码的除符号位外全部取反,补码为反码+1
1.2大小端字节序
- ⼤端(存储)模式:
数据的低位字节保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
字中的字节顺序和原序列的相同。 - ⼩端(存储)模式:
数据的低位字节保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的高地址处。
字中的字节顺序和原序列的相反。
1.3典型习题练习
- 判断当前电脑的大小端字节序
//i的补码 :00000000 00000000 00000000 00000001 //若为小端:00000001 00000000 00000000 00000000 //若为大端:00000000 00000000 00000000 00000001 int check() { int i = 1; return (*(char *)&i); //强转取出第一位数 }
- unsigned char与signed char
int main() { char a= -1; //-1的补码:1111 1111 1111 1111 1111 1111 1111 1111 //a-1111 1111 signed char b=-1; unsigned char c=-1; printf("a=%d,b=%d,c=%d",a,b,c); //打印%d要整形提升,VS默认%d为有符号整形 //a,b整形提升首位补符号位1,而c由于是无符号位,整形提升要补0 return 0; }
-
unsigned char的数据范围:0~255
-
signed char的数据范围:-128~127
-
解题要领:内存中存储的一视同仁都为二进制。不同的数据类型只是充当读出的一个角度。
-
练习1
#include <stdio.h> int main() { char a = -128; //a的补码:1000 0000 printf("%u\n",a); //整形提升:1111 1111 1111 1111 1111 1111 1000 0000 //无符号数直接打印上述数字 return 0; }
-
练习2
#include <stdio.h> int main() { char a = 128; //128的源码:0000 0000 0000 0000 0000 0000 1000 0000 //a-1000 0000 (实际上是-128) printf("%u\n",a); //整形提升:1111 1111 1111 1111 1111 1111 1000 0000 //无符号数直接打印上述数字 return 0; }
- 练习3
int main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); //代码结果为255 return 0; }
总结:
-
综合练习:求在 X86环境下⼩端字节序 的机器中,下面代码输出的结果
#include <stdio.h> int main() { int a[4] = { 1, 2, 3, 4 }; int *ptr1 = (int *)(&a + 1); int *ptr2 = (int *)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2); return 0; }
二、浮点数的存储
2.1浮点数存储的国际标准
根据国际标准IEEE 754
任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^S ∗ M ∗ 2^E
- (−1)^S 表⽰符号位,当S=0,V为正数;当S=1,V为负数。
- M 表⽰有效数字,M是⼤于等于1,⼩于2的。
- 2^E 表⽰指数位
其他规定
- 单精度浮点型:对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
- 双精度浮点型:对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
2.2浮点数如何存入内存
- 先转为二进制,把它写成1.XXXXXXXX的形式
- 为节省空间,只保存小数位
- IEEE 754规定:存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
2.3浮点数如何取出
- E全为0:数字趋于0,不再自动加上第⼀位的1
- E全为1:表⽰±⽆穷⼤
- E不全为0不全为1:指数E的计算值减去127/1023,得到真实值,再将有效数字M前加上第⼀位的1
2.4练习
#include <stdio.h>
int main()
{
int a = 9;
float* pf = (float*)&a;
printf("a的值为:%d\n", a);
printf("*pf的值为:%f\n", *pf);
*pf = 9.0;
printf("a的值为:%d\n", a);
printf("*pf的值为:%f\n", *pf);
return 0;
}
2.5浮点数间的比较
- 引入:
int main()
{
double x = 1.0;
double y = 0.1;
printf("%.50lf\n", x - 0.9);
printf("%0.50lf\n", y);
return 0;
}
由此可见,浮点数不能直接进行比较,因为浮点数本身会有精度损失。
- 那么如何进行比较?
- 自定义精度,通常采用define来定义
- 推荐使用系统精度(头文件<float.h>定义)
3.代码示例(C语言)
#include<stdio.h>
#include<math.h>
#include<float.h>
int main()
{
double x = 1.0;
double y = 0.1;
if ((fabs(x - 0.9) - y) < DBL_EPSILON)
{
printf("yes!");
}
return 0;
}
三、不同类型的数据计算规则
不同类型数的混合运算时,遵循的原则是“类型提升”,即较低类型转换为较高类型。如 long型与 int 型一起运算时,需先将 int 型转换为 long型,然后进行运算,结果为long 型。若float型和double 型一起运算,虽然两者同为浮点型,但精度不同,则仍需先将float型转换为double型后再进行运算,结果亦为double型。所有这些转换都是系统自动进行的,这种转换称为隐式类型转换。