数据在内存中的存储练习题

数据在内存中的存储练习题

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. 总结

在这里插入图片描述

在这里插入图片描述

  1. 有符号的char取值范围是-128~127,当要超过127时,值会-128开始
    1 2 3 … 125 126 127 -128 -127 -126 … -3 -2 -1 0 1 2 3 …
    其他数据类型也一样

  2. 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

  3. 有符号的char整型提升时补符号位
    例如:-1
    1111 1111
    -1的补码:1111 1111 1111 1111 1111 1111 1111 1111
    -1的原码:1000 0000 0000 0000 0000 0000 0000 0001
    打印出来就是-1

  4. 无符号的char整型提升时补0
    例如:-1
    1111 1111
    0000 0000 0000 0000 0000 0000 1111 1111
    打印出来就是255

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

4U247

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值