一、整形在内存中的存储
首先先要了解原码、反码和补码。
原码:直接将二进制按正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反即可。
补码:反码加一就得到补码,正数的原、反补码都相同。
对于整形来说:数据存放内存中其实存放的是补码。
VS的debug模式就会把局部变量后面填充一些0xcc。
但是我们会发现顺序有点不对劲,这是为什么呢?
二、大小端介绍
什么是大端小端:
大端(存储模式):是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
网络传输的字节序固定使用大端。
设计一个小程序来判断当前机器的字节序:
代码如下(示例):
#include<stdio.h>
#include<stdlib.h>
int check_sys() {
int i = 1;
return(*(char*)&i);
}
int main() {
int ret = check_sys();
if (ret == 1) {
printf("小端\n");
}
else {
printf("大端\n");
}
system("pause");
return 0;
}
练习1:
#include<stdio.h>
#include<stdlib.h>
int main() {
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
system("pause");
return 0;
}
把char强转为int时,前面高位的三个字节,填充的都是符号位,char和signed char都是有符号的,所以直接补一,unsigned char是无符号的,所以高位补零。
结果:
练习2:
#include<stdio.h>
#include<stdlib.h>
int main() {
char a = -128;
printf("%u\n", a);
system("pause");
return 0;
}
%u是打印unsigned int(无符号十进制的整数)。
char的表示范围:-128->127
int->unsigned int这个转换过程中,数据的二进制表示在内存不发生任何变化。
char自身是有符号的,高位是一,所以高位全补一。所以结果很大。
练习3:
#include<stdio.h>
#include<stdlib.h>
int main() {
char a = 128;
printf("%u\n", a);
system("pause");
return 0;
}
char得到不了+128,给127(0111 1111)+1的话得到1000 0000,其实已经溢出,得到的是-128。所以结果与-128相同。
(char会溢出,int也会溢出,只要你这个类型是有上限的,都会溢出,这是非常常见的)
python里面的int默认是4个字节,但是如果计算过程中发现溢出了,会自动扩容。只要内存足够,就能表示足够大的数字。
练习4:
#include<stdio.h>
#include<stdlib.h>
int main() {
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
system("pause");
return 0;
}
触发隐式类型转换,int转unsigned int
练习5:
#include<stdio.h>
#include<stdlib.h>
int main() {
unsigned int i = 0;
for (i = 9; i >= 0; i--) {
printf("%u\n", i);
}
system("pause");
return 0;
}
unsigned int 没有小于零的情况,所以i>=0这个条件写了和没写一样。
unsigned 类型是特别容易溢出的,原则就是,能不用就不用。Java、Python根本就没有unsigned。
练习6:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++) {
a[i] = -1 - i;
}
printf("%d", strlen(a));
system("pause");
return 0;
}
\0是ASCII值为零的那个字符,(a[i]=-1-i)int赋值给char会出现截断的情况。
所以i为255的时候,-1-255就是‘\0’.所以下标为255的位置被设为了‘\0’,此时求字符串长度就是255。
练习7:
#include<stdio.h>
#include<stdlib.h>
int main() {
short num = 32767;
short int a = num + 1;
printf("%d\n", a);
system("pause");
return 0;
}
练习8:
#include<stdio.h>
#include<stdlib.h>
unsigned char i = 0;
int main() {
for (i = 0; i <= 255; i++) {
printf("hello world\n");
}
system("pause");
return 0;
}
i=255再加一会溢出
所以这个程序是个死循环。
三、浮点型在内存中的存储
IEEE754标准描述了浮点数在内存中如何存储。
1.float
一个浮点数在计算机里,是用“科学计数法”的方式来表示的,只不过不是10的几次方,而是2的几次方。以二为底,算二的E次方。
S是符号位,浮点数默认是有符号的。
2.double
E越大,能表示的数据范围就越大,M越大,能表示的数据精度就越高。
因此实践中还是优先考虑使用double这个类型。
浮点数存储的这个标准,不是C语言标准规定的,其他编程语言也遵守。
重点来啦:浮点数去表示一些特殊的数字的时候才是精确的,很多时候是不能精确表示的。就会导致两个浮点数直接进行比较相等,可能就会出现问题。
例如:
#include<stdio.h>
#include<stdlib.h>
int main() {
double a = 1.6;
double b = 0.3;
double c = a + b;
double d = 1.9;
if (c == d)
printf("相等\n");
else
printf("不相等\n");
system("pause");
return 0;
}
a:
b:
计算机按照IEEE754标准来表示浮点数时,很多数字不能精确表示。所以在使用浮点数时,不能直接比较,如果必要比较相等,可以设立误差。(把两个待比较的数字做差,判定差值是否在一个允许的误差范围之内)
例如:
#include<stdio.h>
#include<stdlib.h>
int main() {
double a = 1.6;
double b = 0.3;
double c = a + b;
double d = 1.9;
if (c-d<0.001&&c-d>-0.001)//需要手动指定一个允许的误差范围
printf("相等\n");
else
printf("不相等\n");
system("pause");
return 0;
}