栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

深度剖析补码和数据在内存中的存储|C语言

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

深度剖析补码和数据在内存中的存储|C语言

目录

1. 数据类型

2. 整形在内存中的存储

2.1进制与权重

 2.2数制与码制

 2.3原码,补码与反码

3.大小端字节序

4.练习


1. 数据类型

整型:

char

        unsigned char

        signed char

short

        unsigned short [int]

        signed short [int]

int

        unsigned int

        signed int

long

        unsigned long [int]

        signed long [int]

注:[int]代表int可以省略。所有的short,int,long都是默认为signed类型。char类型既可以为unsigned类型也可为signed,具体取决于编译器,vs2019里为signed类型。

浮点数:

 float

double

构造类型:

> 数组类型

> 结构体类型 struct

> 枚举类型 enum

> 联合类型 union

指针类型:

int *pi;

char *pc;

float* pf;

void* pv;

注:void 表示空类型(无类型) 通常应用于函数的返回类型、函数的参数、指针类型。

2. 整形在内存中的存储

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。 那接下来我们谈谈数据在所开辟内存中到底是如何存储的?

2.1进制与权重

进制也就是进位记数制,是人为定义的带进位的计数方法。 对于任何一种进制---X进制,就表示每一位上的数运算时都是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位。

计算机里存储数据都是采用的二进制。

 2.2数制与码制

数制:我们把多位数码中每一位的构成方法以及从低位到高位的进位规则称为数制。
码制:为了便于记忆和查找,在编制代码时总要遵循一定的规则,这些规则就称为码制。

常用的数制有:二进制,八进制,十进制等

由于数据需要表示不止是正数,还有负数。为了表示负数,规定最前面一个不是对应权重而是表示正负,叫做符号位,剩下的表示数值叫做数值位。符号位为1代表负数,为0代表正数。这里的符号位代表正负,为码制。

例如:-5的二进制

2.3原码,补码与反码

如果按照最前面放符号位来处理二进制,就会出现两个问题。

第一:这样二进制数第一个数值代表符号是码制,后面的数值位是数制,整体就是数制与码制的混合编码。对于计算机而言,只能对数制进行加减运算无法直接处理带符号位的二进制数。

例如:(-5)+(5)显然是不会等于-10的。

第二:出现了+0和-0

为了解决上述两个问题,出现了补码,把首位改成数制而不是码制,不过权重发生了变化。这样二进制就全都是数制编码,可以直接进行数字运算。

 

如果10000000是有符号数的话,转为十进制就是-128(1*-2^7+0*2^6+……)。1000就是-8。

这时就可以二进制位数直接相加。

这样子不太适合我们求出二进制的补码。因此出现了反码 。

把最前面一位记为符号位时得出的二进制数为原码。反码通常是用来由原码求补码或者由补码求原码的过渡码。反码跟原码是正数时,一样;负数时,反码就是原码符号位除外,其他位按位取反。 将反码加一就得到了补码。

对于整形来说:数据存放内存中都存放的是补码。使用补码,可以直接对二进制进行运算处理。CPU只有加法器,会将减法转化为加上对应的负数。

3.大小端字节序

 可以看出a的存储与我们想的不一样。为什么呢?

因为在计算机系统中,以字节为单位,每个地址单元 都对应着一个字节,一个字节为8 bit。但是在C语言中还有16 bit的short 型,32 bit的long型(要看具体的编译器)大于一个字节,另外,对于位数大于8位的处理器,例如16位或者32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因 此就导致了大端存储模式和小端存储模式因为计算机的存储时有两种存储方式,叫做大小端字节序。

大端序(Big-Endian)将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址。 这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。

小端序(Little-Endian),将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称 小端序 。 小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。

注:低位字节和高位字节

 可以看出vs2019是小端存储。

4.练习

1.设计一个小程序来判断当前机器的字节序。

 思路:存入值为1的变量拿到低地址的第一个字节的内容判断是否为1。

//代码1
#include 
int check_sys()
{
 int i = 1;
 return (*(char *)&i);
}
int main()
{
 int ret = check_sys();
 if(ret == 1)
 {
 printf("小端n");
 }
 else
 {
 printf("大端n");
 }
 return 0;
}
//代码2
int check_sys()
{
 union
 {
 int i;
 char c;
 }un;
 un.i = 1;
 return un.c;
}

2.

//输出什么?
#include 
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;
}

 过程解析:

 char a = -1;
    
    signed char b = -1;
    
    unsigned char c = -1;
    


    printf("a=%d,b=%d,c=%d", a, b, c);
    //对于小于四个字节的类型再遇到 %d%u 或者进行算术运算时,就会发生整形提升:
    //此时整形提升的规则是把一个字节的内容取出来如果是signed类型补符号位,unsined类型补0
    
    
    

3.

//输出什么?
#include 
int main()
{
    char a = -128;
    printf("%un",a);
    return 0;
}

 过程解析:

 char a = -128;
    //100000000     a内存
    printf("%un", a);
    //11111111 11111111 11111111 10000000  整形提升
    //%d --->将数值看成无符号的类型进行读数并打印其十进制数值
    //无符号原码
    //11111111 11111111 11111111 10000000
    //

 4.

#include 
int main()
{
    char a = 128;
    printf("%un",a);
    return 0;
}

 过程解析:

  char a = 128;
    
   
    
    printf("%un", a);
    //11111111 11111111 11111111 10000000  整形提升
    //无符号的原码
    //11111111 11111111 11111111 10000000   
    //

5.

int i= -20;
unsigned  int  j = 10;
printf("%dn", i+j); 
//按照补码的形式进行运算,最后格式化成为有符号整数
//输出什么?

 过程解析:

	int i = -20;
	//100000000 00000000 00000000 00010100   原
	//111111111 11111111 11111111 11101011   反
	//111111111 11111111 11111111 11101100   补
	unsigned  int  j = 10;
	//000000000 00000000 00000000 00001010   原,反,补

	printf("%dn", i + j);
	//111111111 11111111 11111111 11101100
	//000000000 00000000 00000000 00001010
	//111111111 11111111 11111111 11110110  i+j 补码
	//111111111 11111111 11111111 11110101  反码
	//100000000 00000000 00000000 00001010  原码  -10

6.

#include
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%un", i);
	}

	return 0;
}

 结果:无限循环打印

  过程解析:

	for (i = 9; i >= 0; i--)
	{
	
		
		//000000000 00000000 00000000 00001001 9
		//000000000 00000000 00000000 00001000 8
		//000000000 00000000 00000000 00000111 7
		//000000000 00000000 00000000 00000110 …
		//000000000 00000000 00000000 00000101
		//000000000 00000000 00000000 00000100
		//000000000 00000000 00000000 00000011
		//000000000 00000000 00000000 00000010
		//000000000 00000000 00000000 00000001
		//000000000 00000000 00000000 00000000 0
		// 接下来i-1的结果不是-1,而是4294967295(无符号的0xFFFFFFFF)
		
		//11111111 11111111 11111111 111111111	
		//11111111 11111111 11111111 111111110
		//……
		//10000000 00000000 00000000 000000000
		//……
	
		printf("%un", i);
	}

7

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

   过程解析:

​
    char a[1000] = { 0 };
    int i = 0;
    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    //1111 1111   -1
    //1111 1110   -2
    //1111 1101   -3
    //……
    //1000 0000   -128
   //10111 1111   -129  截取
    //0111 1111   127
    //0111 1110   126
    //0111 1101   125
    //……
    //0000 0001   1
    //0000 0000   0   本质为0

​

8

#include 
unsigned char i = 0;
int main()
{
    for(i = 0;i<=255;i++)
   {
        printf("hello worldn");
   }
    return 0;
}

运行结果:无限打印

    过程解析:

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1037011.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号