C语言(九) 深度剖析数据在内存中的存储 (上)

   之前我们学习了基本的内置类型:

char
short
int
long
float
double

以及它们所占存储空间的大小。

类型的意义:

 1.使用这个类型开辟内存空间的大小(大小决定了使用的范围)

2.如何看待内存空间的视角

类型的归类:

整形:

char
	unsigned char
	signed char
short
	unsigned short[int]
	signed short[int]
int
	unsigned int
	signed int
long
	unsigned long[int]
	signed long[int]

浮点型:

float
double

构造类型:

数组类型
结构体类型  struct
枚举类型   enum
联合类型  union

指针类型:

空类型: void表示空类型(无类型)

             通常应用于函数的返回类型、函数的参数、指针类型

整形在内存中的存储

     我们之前讲过一个变量的创建要在内存中开辟空间的。空间的大小是根据不同类型而决定的。

那接下来我们来谈谈数据在所开辟内存中到底是如何存储的?

比如说:

int a = 20;
int b = -10;

我们知道为a分配四个字节的空间。

那它是 如何存储的呢?

下来我们了解一下原码、反码和补码的概念:

原码:直接将二进制按照正负数的形式翻译成二进制就可以了。

反码:将原码的符号位不变,其他位依次按位取反就可以了。

补码:反码+1就得到了补码。

正数的原码、反码、补码相同

对于整形来说,数据存放内存其实都是存放的是补码。

为什么呢?

     在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值位统一处理。

同时,加法和减法也可以统一处理(CPU只有加法器),此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。


我们可以看到对于a和b分别存储的是补码。但是我们发现顺序不太对。这是为什么呢?

这个时候我们就引入一个大小端的概念。

什么是大小端呢?

大端(存储)模式,是指数据的低位保存在内存的高地址,数据的高位保存在内存的低地址。

小端(存储)模式,是指数据的低位保存在内存的低地址,数据的高位保存在内存的高地址。

如下图:


请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。


代码1:


int main()
{
	int num = 1;
	char* pc = (char*)#
	if (*pc == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	system("pause");
	return 0;
}

代码2:

int check_sys()
{
	int num = 1;
	char* pc = (char*)#
	return *pc;
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	system("pause");
	return 0;
}

代码3:

int check_sys()
{
	int num = 1;
	return *(char*)#
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	system("pause");
	return 0;
}

代码4:

int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	system("pause");
	return 0;
}

结果为 :  小端

下面的代码输出什么呢?

代码1:

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;
}

结果为   a=-1   b=-1   c=255

说明char和signed char范围相同

代码2:

int main()
{
	char c = -128; 
	printf("%d\n", c);
	system("pause");
	return 0;
}

结果为   -128

代码3:

int main()
{
	char c = -128;
	printf("%u\n", c);
	system("pause");
	return 0;
}

结果为: 4294967168

代码4:

int main()
{
	char c = 128; 
	printf("%d\n", c);
	system("pause");
	return 0;
}

结果为   -128

代码5:

int main()
{
	char c = 128;
	printf("%u\n", c);
	system("pause");
	return 0;
}

结果为: 4294967168

代码6

int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	system("pause");
	return 0;
}

结果为 -10

总结:

unsigned char  范围是 0~255

char /signed  char 范围是 -128~127

我们可以看一下溢出问题:

1126127


126127是范围之内的数字,他们的值是遵从规则的。所以他们的值也是126127,并没有越界。

2128

128这个数字是越界的数字,是127+1


这时会在127的最后8位上加1,也就是在0..0 0111 1111的后80111 1111上加1,变为1000 0000。倒数第8位变成了1,所以前24个位也全部变为1,所以该数被表示为负数。它的值为 -1*2^7+0=-128


根据负数的值在计算机中是以补码的形式存储的理论,也就是说1000 0000是补码,即:-128在计算机中是以1000 0000存储的,-128的原码是1000 0000(补码)减1,再取反,结果还是1000 0000(原码)。

3129

129128的取值也是一样的道理。129127+2或者128+1,抛开前24位不说,它的二进制为1000 0001,倒数第8位为1,所以前24位也为1,并且为负数。所以它的值表示为:-1*2^7+1=-127.


根据负数的值在计算机中是以补码的形式存储的理论,1000 0001是补码,即:-127在计算机中是以1000 0001存储的,-127的原码是1000 0001(补码)减1,再取反,结果是0111 1111(原码)。

4-127-128

这两个数字都是在范围之内 没有什么可说的了吧,并且它们的二进制形式也刚刚讲过了。

5-129

-129-128-1所得,所以-128的最后8位是1000 0000,减去1变为0111 1111,前面24位全部置位0,且该数被解释为正数,其值为2^0+2^1+2^2+2^3+2^4+2^5+2^6=2^71=127

6-130

-127的最后8位是1000 0001,减去3变为0111 1110,前面24位全部保持0,且该数被解释为正数,其值为2^1+2^2+2^3+2^4+2^5+2^61=126



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值