C语言题目与练习解析:配套《数据在内存中的存储》


前言:

本篇是上一篇《数据在内存中的存储》的练习篇,为了缩减上一篇的篇幅,将练习篇特意摘出来供大家深入思考



正文:

1.4.2 练习2

#include <stdio.h>
int main()
{
 char a= -1;
 signed char 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 = -1 一个字节
//a = 0x10000001 
//----->补码:0x11111111 
//----->整数提升,默认有符号,前面补1:0x11...11111111
//----->原码:10000...00000001----->最终结果:-1
//b与a同理,结果也为-1
//c与ab前面相同
//整数提升时前面补0:0x00...11111111
//----->原码与补码相同:0x00...11111111----->最终结果:255

1.4.3 练习3

#include <stdio.h>
int main()
{
 char a = -128;
 printf("%u\n",a);
 return 0;
}

运行结果:

4294967168

解析:

//char a = -128 一个字节
//好像一个字节无法放得下-128,那先让我们来看一下char(默认有符号)是如何储存的

实际上,我们可以看一下这个存储方式,每一个表格就代表了一个bit,在第一个数字代表的是符号位的情况下,我们就能发现除了0x10000000以外,其他都很正常,可以理解为一个循环

在这里插入图片描述

其实在内部的定义里面这个0x10000000被认为是 -128 而非 “-0”
在这里插入图片描述
这样子的话这个-128就很好处理了

//a的补码:0x10000000
//----->整数提升:0x11...10000000
//----->输出:4294967168

为什么这里不需要再由原码转换为补码呢?
因为 %u 是用于输出无符号整数的格式符,而无符号整数在计算机中没有原码、反码、补码的区分—— 它们的二进制表示就是其本身的数值形式,直接对应十进制的非负整数。

1.4.4 练习4

#include <stdio.h>
int main()
{
 char a = 128;
 printf("%u\n",a);
 return 0;
}

运行结果:

4294967168

解析:

//char a = 128 同样是一个字节
//存的也就是`0x10000000`,而如果以`%u`的形式输出,结果必然和上面是一样的

1.4.5 练习5

#include <stdio.h>
int main()
{
	char a[1000];
	int i;
	for(i=0; i<1000; i++)
	{
		a[i] = -1-i;
	}
	printf("%d",strlen(a));
	return 0;
}

运行结果:

255

解析:

按照常规结果来说的话,大家一定认为是输出随机值,因为根本没有\0,但是别忘了我们刚才分析的有符号char类型,这里当strlen(a)=255时,这里的a[i]此时也刚好为255,而再加1就会变成0(溢出)0x10000000
也就是说,在256之前就会循环停止,这就导致了输出255

1.4.6 练习6

#include <stdio.h>
unsigned char i = 0;
int main()
{
 for(i = 0;i<=255;i++)
 {
 printf("hello world\n");
 }
 return 0;
}

运行结果:

hello world
hello world
hello world
...
//死循环

解析:

为什么会造成死循环呢?
我们知道无符号char的范围是0-255

在这里插入图片描述

而注意函数循环到最后是256,当 i = 256时,由于循环溢出就会导致归零,死循环就形成了

1.4.7 练习7

#include <stdio.h>
int main()
{
 unsigned int i;
 for(i = 9; i >= 0; i--)
 {
 printf("%u\n",i);
 }
 return 0;
}

运行结果:

//对于运行结果想必大家也猜到了
9
8
7
6
5
4
3
2
1
0
4294967295
4294967294
//...循环

解析:

这个循环是怎么造成的呢?
其实就是当 i = -1 的时候由于是无符号整型,就导致了实际上系统识别出来的是4294967295
从而造成了死循环

1.4.8 练习8

#include <stdio.h>
//X86环境 ⼩端字节序 
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;
 //%x意思是16进制输出
}

运行结果:

42000000

解析:

对于*ptr1[-1]来说,小端字节序,第一个&a处存的就是4,而&a+1处存的就是3,ptr1[-1]处存的也就是4
对于*ptr2来说,(int *)((int)a + 1)将a处地址强转为int类型,然后加1,再强转为int*
类型
仔细分析(int*)(int)a+1就相当于是在基础上向后偏移一个字节,这个字节由于小段排序,刚好就是 0x 02 00 00 00 ,这就导致了输出为2000000

2.1.1 练习9

#include <stdio.h>
int main()
{
 int n = 9;
 float *pFloat = (float *)&n;
 printf("n的值为:%d\n",n);
 printf("*pFloat的值为:%f\n",*pFloat);
 *pFloat = 9.0;
 printf("num的值为:%d\n",n);
 printf("*pFloat的值为:%f\n",*pFloat);
 return 0;
}

运行结果:

n的值为:9
*pFloat的值为:0.000000
num的值为:1091567616
*pFloat的值为:9.000000

解析:

//对于int n = 9用%d输出和*float = 9.0用%f输出,这两个都是正常输出
//而对于int n = 9用%f输出和*float = 9.0用%n输出,我们需要先了解一下浮点数的存储规则,这里只直接说明

IEEE 754规定:

  • 对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
  • 对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

即:

在这里插入图片描述
如图,float类型浮点数在内存中是这样分配的

接下来我们正常分析 int n = 9是这样的存储的:
补码:00000000 00000000 00000000 00001001
解析时他会这样子:0 00000000 00000000000000000001001
即浮点数:1.084×10^-38
这样的话已经非常接近于0了,近似于0

同理:*pFloat = 9.0 是
补码:0 10000010 00100000000000000000000
直接%d输出的话就会自动识别为整型
即整数:1091567616

  • 本节完…
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值