数据在内存中的存储练习题
文章目录
- 数据在内存中的存储练习题
- 1. 练习一
- 2.练习二
- 3. 练习三
- 4. 练习四
- 5. 练习五
- 6. 练习六
- 7. 总结
1. 练习一
#include <stdio.h>
int main()
{
char a = -1;
signed b = -1;
unsigned char c = -1;
printf("a = %d b = %d c = %d", a, b, c);
return 0;
}
代码运行结果:
a = -1 b = -1 c = 255
解释:
char默认是有符号的
首先 a 和 b 都是有符号的char
-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
由于char只有1个字节,也就是8个bit位,所以会发生截断
char中-1的值为 1111 1111
%d打印的是有符号的整数,会将char类型整型提升至int类型
由于c是无符号的char,整型提升时高位补0
0000 0000 0000 0000 0000 0000 1111 1111
将这个二进制翻译成10进制就是255
所以为255
2.练习二
#include <stdio.h>
int main()
{
char a = -128;
printf("%u", a);
return 0;
}
代码运行结果:
4294967168
解释:
-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只有一个字节,会发生截断
-128: 1000 0000
%u打印的是无符号的整数
按照原来的类型进行整型提升
由于char是有符号的char,整型提升时高位补符号位
1111 1111 1111 1111 1111 1111 1000 0000
将这个二进制翻译成十进制就是要打印的数值,这是个相当大的数
可以打开电脑自带的计算器,切换成程序员模式
打印的数就是4294967168
3. 练习三
#include <stdio.h>
int main()
{
char a = 128;
printf("%u", a);
return 0;
}
代码运行结果:
4294967168
解释:
128的原码:0000 0000 0000 0000 0000 0000 1000 0000
128的反码:0111 1111 1111 11111 1111 11111 0111 11111
128的补码:0111 1111 1111 11111 1111 11111 1000 0000
char只有一个字节,发生截断
128: 1000 0000
char的取值范围只有-128~127,存不下128
char中128的值和-128一样,所以系统会认为我们存的是-128
所以打印的值和练习二一样4294967168
4. 练习四
#include <stdio.h>
#include <string.h>
int main()
{
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%zd\n", strlen(a));
return 0;
}
代码运行结果:
255
解释:
循环1000次,一次赋一个值,但是strlen计算的是’\0’之前的元素个数,'\0’的ASCII就是0
所以要在循环中找到第一个0的位置,char类型的取值范围是-128~127
当char中的值为127时再加一,值就变成了-128
当char中的值为-128时再减一,值就变成了127
如此循环
a[i]的值为 -1 -2 -3 -4 … -127 -128 127 126 125 … 3 2 1 0
所以打印的值为128 + 127 = 255
5. 练习五
代码一:
#include <stdio.h>
unsigned int i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("Hello World!\n");
}
return 0;
}
运行结果:
死循环打印Hello World!
代码二:
#include <stdio.h>
#include <windows.h> //使用Sleep需包含的头文件
int main()
{
unsigned int i = 0;
for (i = 9; i >=0 ; i--)
{
printf("%u\n",i);
Sleep(1000); //暂停一秒便于观察
}
return 0;
}
代码运行结果:
死循环打印
9 8 7 6 5 4 3 2 1 4294967295 42949672954 …
解释:
代码一中定义了一个无符号的char,取值范围是0~255,永远不会超过255,当超过255时,又会从0开始,所以造成了死循环
代码二中定义了一个无符号的int,和无符号char同理永远不会超过它的取值范围
0~4294967295,当超过最大值时,就会从0继续开始,如果超过最小值0时,又会从最大值开始
6. 练习六
#include <stdio.h>
int main()
{
int a[4] = { 1,2,3,4 };
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
printf("%x,%x", p1[-1], *p2);
return 0;
}
在VS2022,X86的环境下,代码运行结果:
4,200000
解释:
&a取出的是数组的地址,+1跳过了整个数组,p1指向4后的地址
p1[-1] 等价于*(p1 - 1),p1是int*类型的,-1后退4个字节,整型数组中的元素也是4个字节,所以p1从指向4后的地址变成了指向4,所以p1[-1]是4,打印十六进制也是4
a表示首元素的地址,将地址转化为int类型的,+1,也就是整数+整数,假设a的地址为0x010,转化为int类型,就是16,+1等于17,转化为十六进制就是0x011,p2的地址从0x010变成了0x011,地址只加了一个字节,也就是跳过了一个字节,p2指向首元素的第二个字节的位置,int解引用访问四个字节,从首元素的第二个字节的位置向后访问四个字节
访问得到 00 00 00 02,由于小端字节序是低字节存放在低地址,所以是倒着放的,转化为十六进制要反过来02 00 00 00,所以打印的是02000000
7. 总结
-
有符号的char取值范围是-128~127,当要超过127时,值会-128开始
1 2 3 … 125 126 127 -128 -127 -126 … -3 -2 -1 0 1 2 3 …
其他数据类型也一样 -
char类型的二进制会截断,不管前面是什么,只管最后8个bit的数值
例如:-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
截断之后1111 1111
只会存最后8个bit位,这时候就要看最高位是符号位还是数值位
有符号值就为-1
无符号就为255 -
有符号的char整型提升时补符号位
例如:-1
1111 1111
-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
打印出来就是-1 -
无符号的char整型提升时补0
例如:-1
1111 1111
0000 0000 0000 0000 0000 0000 1111 1111
打印出来就是255