一. 整数在内存中的存储:
1.大小端字节序的断定:
#include <stdio.h>
#include <stdlib.h>
int IsLittleEnd() {
int a = 0x11223344;
int* p1 = &a;
char* p2 = (char*)&a;
if (*p2 == 0x44) {
return 1;
}
return 0;
}
int main () {
printf("%d\n",IsLittleEnd());
system ("pause");
return 0;
}
运行结果:
负整数在内存的存储: 负数以补码的形式存储
2.针对不同类型,判定具体的存储的方式
问题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\n", a, b, c);
system ("pause");
return 0;
}
具体求步骤:
%d 表示打印的是一个有符号的十进制的整数.
1>针对char a=-1 求解过程:
第一步:
先将a=-1的二进制画出来 int占4个字节32个bit位
a = -1; -1原码:1000 0000 0000 0000 0000 0000 0000 0001
- -1反码:1111 1111 1111 1111 1111 1111 1111 1110
- -1补码:1111 1111 1111 1111 1111 1111 1111 1111
-1在二进制中以补码的形式存储:1111 1111 1111 1111 1111 1111 1111 1111
第二步:
因为a的前面是char类型 1个字节占8个bit位
char a=-1的二进制位:1111 1111
第三步:
因为%d打印的是一个十进制有符号的整数(int)
1111 1111 1111 1111 1111 1111 1111 1111 (最高位补充的符号位) 因为char只有8个bit位,而int占32个bit位,因此需要补充符号位
2>针对signed char b = -1求解过程:
代表的是有符号的类型,因此求解过程和char a=-1完全一样;
第一步:
先将a=-1的二进制画出来 int占4个字节32个bit位
a = -1; -1原码:1000 0000 0000 0000 0000 0000 0000 0001
- -1反码:1111 1111 1111 1111 1111 1111 1111 1110
- -1补码:1111 1111 1111 1111 1111 1111 1111 1111
-1在二进制中以补码的形式存储:1111 1111 1111 1111 1111 1111 1111 1111
第二步:
因为a的前面是char类型 1个字节占8个bit位
char a=-1的二进制位:1111 1111
第三步:
因为%d打印的是一个十进制有符号的整数(int)
1111 1111 1111 1111 1111 1111 1111 1111 (最高位补充的符号位) 因为char只有8个bit位,而int占32个bit位,因此需要补充符号位
3>针对unsigned char c = -1的求解过程:
第一步:
先将a=-1的二进制画出来 int占4个字节32个bit位
a = -1; -1原码:1000 0000 0000 0000 0000 0000 0000 0001
- -1反码:1111 1111 1111 1111 1111 1111 1111 1110
- -1补码:1111 1111 1111 1111 1111 1111 1111 1111
第二步:
因为a的前面是char类型 1个字节占8个bit位
unsigned char a=-1的二进制位: 1111 1111
第三步:
因为%d打印的是一个十进制有符号的整数(int)
0000 0000 0000 0000 0000 0000 1111 1111 (最高位补充的符号位) 因为char只有8个bit位,而int占32个bit位,因此需要补充符号位
unsigned char 代表无符号整数,因此最高位补充的是0.
运行结果:
问题2:
#include <stdio.h>
#include <stdlib.h>
int main () {
char a = -128;
printf("%u\n",a);
system ("pause");
return 0;
}
求解步骤:
第一步:
a=-128的二进制: -128原码: 1000 0000 0000 0000 0000 0000 1000 0000
- -128反码: 1111 1111 1111 1111 1111 1111 0111 1111
- -128补码: 1111 1111 1111 1111 1111 1111 1000 0000
第二步:
对于char a来说对应的二进制占8个bit位,因此从第一步 -128补码中截取8个bit位 1000 0000
第三步:
%u打印的是十进制无符号的整数(int) 1111 1111 1111 1111 1111 1111 1000 0000(最高位补符号位) 但是此时最高位的1不再是符号位 ,最终导致打印结果是一个很大的整数
int ->char->unsigned int
运行结果:
问题3:
#include <stdio.h>
#include <stdlib.h>
int main () {
char a = 128;
printf("%u\n",a);
system ("pause");
return 0;
}
求解过程:
%u代表打印无符号的十进制整数
第一步:
a = -128的二进制 : -128原码 : 1000 0000 0000 0000 0000 0000 1000 0000
-128反码: 1111 1111 1111 1111 1111 1111 0111 1111
-128补码: 1111 1111 1111 1111 1111 1111 1000 0000
第二步:
char a = 128 截取最后8个bit位 1000 0000 (此时最高位1代表的就是符号位,因为假如不是符号位,对于char类型的范围是-128~127,没有+128)
第三步:
%u打印的是十进制无符号的整数(int) 1111 1111 1111 1111 1111 1111 1000 0000(最高位补符号位) 但是此时最高位的1不再是符号位 ,最终导致打印结果是一个很大的整数
运行结果:
强调到底是32位操作系统还是64位操作系统,因为系统不同对应的内存字节大小不同
问题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;
}
第一步:
- -20原码: 1000 0000 0000 0000 0000 0000 0001 0100
- -20反码: 1111 1111 1111 1111 1111 1111 1110 1011
- -20补码: 1111 1111 1111 1111 1111 1111 1110 1100
unsigned int(最高位不是符号位) 1111 1111 1111 1111 1111 1111 1110 1100
第二步:
10原码: 0000 0000 0000 0000 0000 0000 0000 1010
第三步:
- 1111 1111 1111 1111 1111 1111 1110 1100
- 0000 0000 0000 0000 0000 0000 0000 1010
=1111 1111 1111 1111 1111 1111 1111 0110 (最高位是符号位,因为%d) - 1111 1111 1111 1111 1111 1111 1111 0110 取反之后+1 得到本身的值
- 1000 0000 0000 0000 0000 0000 0000 1001取反
- 1000 0000 0000 0000 0000 0000 0000 1010 +1(最高位是符号位)
问题5:
#include <stdio.h>
#include <stdlib.h>
int main () {
unsigned int i;
for (i = 9; i >= 0;i--) {
printf("%u",i);
}
system ("pause");
return 0;
}
i-- = i-=1 = i=i-1; i=0时i=-1
unsigned int i=-1表示一个很大的正数,始终代表>=0
原则上 unsigned int 能不用就不用了,尽量用有符号类型
问题6.strlen 找 \0 (ascill 值为0的那个字符)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main () {
char a[1000];
int i;
for (int i = 0; i < 1000;i++) {
a[i] = -1 - i;
}
printf("%d\n",strlen(a)); //找到\0即可对应的scill码值为0 //255
system ("pause");
return 0;
}
strlen 假如长度是5,那么对应的下标就是5 举例:“abcdef” 长度为6,那么对应下标为6的元素遇到\0.
-1 - i=0; -1原码: 1000 0000 0000 0000 0000 0000 0000 0001
- -1反码: 1111 1111 1111 1111 1111 1111 1111 1110
- -1补码: 1111 1111 1111 1111 1111 1111 1111 1111
1111 1111
+0000 0001
=0
0000 0001 是一个负数 因此求其原码
0000 0000 0000 0000 0000 0000 0000 0001
取反: - 1111 1111 1111 1111 1111 1111 1111 1110
- 1 1111 1111 1111 1111 1111 1111 1111 1111
又因为是char类型,因此发生截断: 1111 1111
一个字符数组如果没有\0就不是字符串, 就不能使用strlen进行计算(出未定义行为)
问题7.
#include <stdio.h>
#include <stdlib.h>
int main () {
unsigned char i = 0;
for (i = 0; i <= 255;i++) {
printf("hehe\n");
}
system ("pause");
return 0;
}
255+1=256
255原码0000 0000 0000 0000 0000 0000 1111 1111
+1 0000 0000 0000 0000 0000 0000 0000 0001
然后由于i的类型为unsigned char 占一个字节
1111 1111
+0000 0001
1 0000 0000 截取1个字节(8个bit位)又等于0
二.浮点数在内存中的存储
1.值得注意的地方: 当把整数的类型按照float类型来解析时, 会导致解析错误 :
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 9;
int* p = &n;
float* p2 = (float*)p;
printf("%f\n", *p2);
system("pause");
return 0;
}
2.判定浮点数相等易出现的错误点:
两个小数相除很可能是一个无限小数,但是float/double内存空间是有限的.(浮点数在内存空间中的存储是有限的), 实际在写代码的过程中利用进行相减看差值是否小于一个预期的差值范围之内.
#include <stdio.h>
#include <stdlib.h>
int main () {
float i = 19.0;
float j = i / 7.0;
if (i == j*7.0) {
printf("相等\n");
}
else {
printf("不相等");
}
system ("pause");
return 0;
}
看二者的差值是否小于预期的值即可
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define E 1e-4
int main() {
float i = 19.0;
float j = i / 7.0;
if (j*7.0 - i < E && j*7.0 - i > -E) {
printf("相等\n");
}
else {
printf("不相等");
}
system("pause");
return 0;
}
整理:浮点数在内存的存储存在一定的误差,两个浮点数字相除可能是无限的,但是float/double内的空间是有限的.