深度剖析数据在内存中的存储

1.数据的基本内置类型:

char 字符数据类型 1字节
short 短整型 2字节
int 整型 4字节
long 长整型 4字节
long long 更长的整型 8字节
float 单精度浮点数 4字节
double 双精度浮点数 8字节

2.void(空类型)
(1)void不能直接定义变量,但 void* 可以,且在32平台下占4字节。
(2)void* 可以接收任意类型,通常用来接收任意指针类型。

3.整型在内存中的存储
创建变量=开辟空间+赋值,空间的大小是根据其自身类型决定的。

对于整形来说,数据存放内存中其实存放的是补码。补码更利于加法运算,因为CPU只有加法器。
我们需要把数据转换为补码形式,先要给出其原码,除过符号为其他比特位全部取反得到反码,然后再将反码加1得到其补码。
在这里插入图片描述
大小端问题:
我们知道数据在存储时,都是以字节存储的,不同的计算机可能有不同的存储方式,我们把它分为大端存储模式和小端存储模式。

大端:数据的低权值位保存在内存的高地址中。
小段:数据的低权值位保存在内存的低地址中。

之后的内容默认计算机为小端存储模式。

根据以上知识我们通过实例看一看数据到底是怎么存储的。
比如: int a = 20; int b = -10;
我们知道int类型需要分配四字节,但是他们在计算机中是怎样存储的呢?
存数据看数值,且存储数据时是按字节存储的。
(1)int a = 20; 20为正数,正数的原码、反码、补码都一样。

0000 0000 0000 0000 0000 0000 0001 0100(原、反、补码)
   0    0    0    0    0    0    1    4   

因为存储是按照字节存储的,所以20在内存(小端)中这样存放: 14 00 00 00
(2)int b = -10,-10为负数,其符号位为1。

1000 0000 0000 0000 0000 0000 0000 1010 (原码)
1111 1111 1111 1111 1111 1111 1111 0101 (反码)
1111 1111 1111 1111 1111 1111 1111 0110 (补码)
   F    F    F    F    F    F    F    6 

所以,-10在内存(小端)中这样存放:F6 FF FF FF



为了更好的巩固理论知识,我们来做几个练习:

一、输出什么?

#include
#include

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

解题思路步骤:
(1)我们知道,在存数据时只看数值,取其补码,那我们先求-1的补码:

1000 0000 0000 0000 0000 0000 0000 0001(原码)
1111 1111 1111 1111 1111 1111 1111 1110(反码)
1111 1111 1111 1111 1111 1111 1111 1111(补码)

(2)我们得到原码,现在要把它存入变量所开辟的空间中,但我们变量的数据类型都为char,在内存中开辟了一字节的空间。那么在存数据时就会发生截断,截取低八位的数据存入变量中。那现在变量a、b、c中存的数据为1111 1111.

(3)最后输出,我们需要读取数据,读取数据要看变量自身的类型,这题中,a和b都是有符号char型,其结果肯定是相同的。我们再看输出格式要求,%d为有符号十进制打印,所以会发生整型提升

负数整型提升时,高位补充符号位1.
正数整型提升时,高位补充符号为0.
无符号数整型提升时,高位补0.

1)读取a和b,a和b为有符号char型,-1为负数,高位补符号位1,整型提升为

1111 1111 1111 1111 1111 1111 1111 1111(补码)//又因为是%d输出,需要把它转换为原码然后输出,
1111 1111 1111 1111 1111 1111 1111 1110(反码)
1000 0000 0000 0000 0000 0000 0000 0001(原码)

a和b的输出要求为%d,即按照有符号十进制打印,所以a和b的值都为-1.

2)读取c,c为无符号char型,所以整型提升时高位补0.得到
0000 0000 0000 0000 0000 0000 1111 1111(原码、补码、反码)
c的、输出要求为%d,即按照有符号十进制打印,结果为255.

:a=-1,b=-1,c=255.


二、下面程序输出什么?

#include
#include

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

解题思路和步骤
(1)存数据,看数值,取补码。

1000 0000 0000 0000 0000 0000 1000 0000(原码)
1111 1111 1111 1111 1111 1111 0111 1111(反码)
1111 1111 1111 1111 1111 1111 1000 0000(补码)

(2)存数据,变量a类型为char,开辟了一字节空间,截取补码低八位存入a中,即a中为1000 0000.
(3)读数据,看变量自身类型,做整型提升,变量a为有符号char,高位补充符号位-1,即 1111 1111 1111 1111 1111 1111 1000 0000
(4)最后以%u的形式打印,%u是按照无符号十进制打印的,(无符号数原码等于补码),所以这里打印出来的就是1111 1111 1111 1111 1111 1111 1000 0000 的十进制4294967168。

:4294967168。


三、输出什么?

#include
#include

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

解题思路和步骤:
(1)存数据,看数值,取补码

0000 0000 0000 0000 0000 0000 1000 0000(原码、补码、反码)

(2)存数据,char开辟了一个字节空间,发生截断,低八位存入,a中存的是1000 0000
(3)取数据,看a的自身类型,为有符号char,发生整型提升,第八位1被当作符号位,则高位补1,为1111 1111 1111 1111 1111 1111 1000 0000.
(4)%u是按照无符号十进制打印,即1111 1111 1111 1111 1111 1111 1000 0000的十进制,为4294967168。

: 4294967168.


四、输出什么?

#include
#include

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

解题思路和步骤:
(1)存数据,看数值,取反码。

i
1000 0000 0000 0000 0000 0000 0001 0100(原码)
1111 1111 1111 1111 1111 1111 1110 1011(反码)
1111 1111 1111 1111 1111 1111 1110 1100(补码)
j
0000 0000 0000 0000 0000 0000 0000 1010(原码、反码、补码)

i+j
      1111 1111 1111 1111 1111 1111 1110 1100(补码)
 +    0000 0000 0000 0000 0000 0000 0000 1010(补码)
  ————————————————————————————————————————————————————————
 =    1111 1111 1111 1111 1111 1111 1111 0110            

(2)取数据,%d有符号十进制输出,

1111 1111 1111 1111 1111 1111 1111 0110 (原码)
1111 1111 1111 1111 1111 1111 1111 0101 (-1)
1000 0000 0000 0000 0000 0000 0000 1010 (取反)

1000 0000 0000 0000 0000 0000 0000 1010 有符号十进制输出为-10.

:-10.


五、输出什么?

#include
#include

int main()
{
	unsigned int i = 0;
	for (i = 0; i >= 0; i--){
		printf("%u\n", i);
	}
	system("pause");
	return 0;
}

解题思路:
这题数据肯定有一个存储和读取的过程,读取时我们需要看变量的自身类型,很明显i是无符号char型,也就是说,从i中拿到的值将全部为正数且>=0,始终满足for循环的条件,所以此题为死循环。

答:死循环。


六、输出什么?

#include
#include

unsigned char i = 0;

int main()
{
	for (i = 0; i <= 255; i++){
		printf("hello\n");
	}
	system("pause");
	return 0;
}

解题思路:
这道题和上一题是一个题型,i的值要读取,看其类型,为无符号char型,i的取值只能是0-255,一直满足for循环的条件,为死循环。

答:死循环。


七、输出什么?

#include
#include

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++){
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	system("pause");
	return 0;
}

解题思路:
(1)它最后要输出的是数组长度,那最开始我们已经定义了数组长度100,所以它肯定不是单纯的求a数组长度,我们知道,char遇到\0表示字符结束,而\0的ASCII码刚好也是0,所以说,再遍历数组的时候,一定会碰到0,这是结束,取出i。
(2)遇到0是在哪个情况呢,因为char的取值为-128~127,从-1开始直到-18,比如说我们现在是1111 1111(-128)-1=1111 1110(127),此时我们继续循环,1111 1110一直减一,127次后变为0000 0000,遍历结束!

答:128+127=255


你可能感兴趣的:(C语言)