整数的存储原理(C语言)

数据的储存(C语言解释)

一、整形在内存中的存储

1.原码、反码、补码的引入

1.1概念:

①原码:
直接将二进制按照正负的形式翻译成二进制即可。
②反码:
将原码的符号位不变,其他为此按位取反即可。
③补码:
反码+1即是补码。

注:正数原、反、补相同,而负数以补码形式存储在内存当中,所有数据在运算都用其补码进行加减,而在打印出来的时候,打印的是其原码而非补码。
为何要引入反码补码的概念呢?

计算器中只有加法运算,因此1-1的运算如下:
若负数也以原码形式存储,那么1+(-1)的二进制运算形式如下:
0000 0000 0000 0000 0000 0000 0000 0001
+
1000 0000 0000 0000 0000 0000 0000 0001
=
1000 0000 0000 0000 0000 0000 0000 0001
可以看到其结果并不等于0。
而引入补码的概念后:
0000 0000 0000 0000 0000 0000 0000 0001
+
1111 1111 1111 1111 1111 1111 1111 1111
=
1 0000 0000 0000 0000 0000 0000 0000 000
溢出的1被截断,即结果为0。

2.整形的存储

2.1大小端
2.1.1 大小端概念:

①大端模式:
指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中,即a中的数据从下图的4开始存,顺序为4–>3–>2–>1。
②小端模式:
数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中,即a中的数据从1开始存,顺序为1–>2–>3–>4。

我们知道,一个int的开辟有四个连续字节
如下:

在这里插入图片描述

指针p维护开辟的四个字节,而开辟的四个字节也有着高低的顺序,因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节。我们按照上图将其标上序号,即
1,2,3,4。那么a中的数据先存入哪个字节便成了大小端的问题。

2.1.2 例题:

(1)请设计一个小程序来判断当前机器的字节序(小端还是大端?)
代码实现:

#include<stdio.h>
int check()
{
	int a = 1;
	return *((char*)(&a));
}
int main()
{
	if (1 == check())
	{
		printf("小端存储模式\n");
	}
	else if (0 == check())
	{
		printf("大端存储模式\n");
	}
}
原理:
通过强制类型转换,判断int中四个字节的低地址字节所储存的数值,若为1,说明原本数据的低位存储在低地址中,属于小端存储,反之为大端存储。
2.2 char类型存储原理
我们知道, signed char类型的取值范围是-128~127,unsigned char的取值范围是0~255,背后的原理让我们一起来探究探究!
2.2.1 整形提升

定义:C语言的整型算数运算总是至少以缺省整形类型的精度来进行的。
为获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换称为整形提升****(即小于int型的整形在运算时一般都会发生整形提升,产生的结果再发生截断)

整形提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。

整形提升的过程:

①负数的整形提升:
例如char c1=-1;
要整形提升为四个字节,最高位补充符号位:
1111 1111 1111 1111 1111 1111 1111 1111
②正数的整形提升:
char c2=1;
整形提升时,最高位补充符号位,即为0:
0000 0000 0000 0000 0000 0000 0000 0001
③unsigned类型的整形提升:
直接补充0

我们用例子来理解:

实例1

#include<stdio.h>
int main()
{
	char a = 127;
	char b = 1;
	char c = a + b;
	printf("%d\n", c);
	printf("%d\n", a + b);
	printf("%d\n", sizeof(c));
	printf("%d\n", sizeof(a+b));
}

运行结果:
在这里插入图片描述

这里其实就是发生了整形提升,我们知道char类型的范围是-128~127,而当127+1时,其结果却为-128。
整形提升的过程我们用下图来解释:

在这里插入图片描述
在这里插入图片描述
a+b存入c时又发生了截断,所以在用%d打印c的时候又要进行一次整形提升:
在这里插入图片描述
因此,最终打印出c的结果为-128。
而将a+b的结果存储入c中直接打印,编译器自动将其转换为四个字节,这个由运行结果我们可以看出,他们的字节数一个是1,一个是4。

实例

#include<stdio.h>
int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
}

在这里插入图片描述
因为a,b都要发生整形提升,由于补位符号位为1,全变成了负数,只有c未发生整形提升。

2.2.2 char类型的存储

根据上面的整形提升,其实我们就可以知道char他存储整数的过程了,下面我们还是通过一个个实例来理解它的过程:

实例1.下面代码输出结果是什么?

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

运行结果:在这里插入图片描述
可以看到,输出结果为a=-1,b=-1,c=255。

原理:
其中-1都以补码形式1111 1111存储入a,b,c中,但在用%d打印的时候发生的整形提升不一样,a和b为一类,c为一类。
(1)a和b(其中vs编译器下char默认为signed char):
a和b的打印过程为
①整形提升过程:
1111 1111->1111 1111 1111 1111 1111 1111 1111 1111
②反码转原码过程(由于最高符号位为1,需转换为原码):
1111 1111 1111 1111 1111 1111 1111 1111–>
1000 0000 0000 0000 0000 0000 0000 0001
即为-1
(2)c的打印过程:
①整形提升:
1111 1111->
0000 0000 0000 0000 0000 0000 1111 1111
(unsigned补0)
②由于最高位符号位为0,原码=补码
0000 0000 0000 0000 0000 0000 1111 1111=255。

实例2 下面代码输出结果为?

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

结果:
在这里插入图片描述

char 类型的范围不是-128~127吗?为什么呢可以打印出这么大的数呢?
其实就是前面说的,在打印整数的时候要进行整形提升。
存储入a中为-128的补码形式:1000 0000
①整形提升->
1111 1111 1111 1111 1111 1111 1000 0000
②打印->
%u打印为无符号类型,即原码=补码
1111 1111 1111 1111 1111 1111 1000 0000=4294967168

实例3

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

结果:
在这里插入图片描述

a中存储截断后的128补码形式->
1000 0000
%u打印直接打印其整形提升后的补码->
1111 1111 1111 1111 1111 1111 1000 0000=4294967168

思考一下,如果这里用%d打印,结果是多少?

实例4

#include<stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
}

结果:
在这里插入图片描述

还是一样,i+j按照补码的形式进行运算,最后格式化用%d打印的时候又因其最高符号位为1而打印其原码形式。

实例5

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

结果:
在这里插入图片描述

当i<0时,由于i为unsigned类型,在for里面判断结果始终是正数,用%u打印的结果和上面例子一样,就不叙述了。

实例6

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

结果:
在这里插入图片描述
原理如下图:
在这里插入图片描述

分为unsigned和signed两类:
①unsigned类型:
因为无符号位的存在,255+1—>0
②signed类型:
有符号位,因此以符号位为分界线,当0111 1111加上1时,由127----->-128;

总结:共同的分界线都是当加到1111 1111时,由于发生截断,再加1时就变成了0。而signed还有一个分界线就是符号位的存在。

而实例6就是找到数组里面的第一个0,按照上述原理,第255个为0.

实例7

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

弄清楚实例6,那么实例7的结果可想而知,是死循环打印“hello world”
结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值