C-数据在内存中的储存

本文详细探讨了C语言中不同整型在内存中的存储方式,包括原码、反码、补码的概念,以及大小端字节序的影响。讲解了整型和浮点型数据的存储规则,并提到了数据类型溢出和浮点数比较的注意事项。此外,还简单介绍了构造类型如数组、结构体、枚举和联合体。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言中有很多的内置数据类型,不同的数据类型在内存中的存储方式也是不一样的,同一类型在不同环境下的存储方式也是不一样的。

整型:

char-unsigned char -signed char

long-unisgned long (int) -signed long (int)

short-unsigned short (int)-signed (int)

int-unsigned int -signed int

除了char以外,其他整型类型默认为signed,如要使用unsigned类型,则声明。

char虽然是字符类型,但在内存中以ASCLL码值的形式存储,ASCLL为一个整数。

char默认值并不一定为unsigned或者signed,这取决于不同编译器的实现。

有正负的数据可以放在signed中,无符号的放在unsigned中(被默认看为正值)。

有符号的数据其二进制序列最高位为符号位:0-正,1-负。

而无符号的数据二进制最高位为数据位,这就扩大了该类型数据的存储大小范围。

整型在内存中的存储:
整型在内存中通常申请四个字节的空间来存储。

下面提出概念:原码,反码与补码

符号位:0-正数,1-负数。

原码:一个整型的值由十进制数转化为二进制得来的二进制序列。

注意:正数的原码反码补码相同。

反码:如果整型值为负,则按这个负数的原码中符号位不变其他位按位取反得来。

补码:由反码加1得来。

根据逆运算,我们可以实现从补码到原码的转换,或者让补码再符号位不变按位取反加1后也可得到原码。
数据在内存中存储的是其补码。

补码的二进制序列可以根据字节数bit位来分模块变成十六进制的数据,可在调试监视器中输入变量地址查看对应变量的数值。

比如数组-10:其二进制序列

原码:10000000000000000000000000001010

反码:111111111111111111111111111111110101

补码:111111111111111111111111111111110110

在内存中分为:1111 1111 1111 1111 1111 1111 1111 1111 0110

                          F      F      F      F      F      F      F      F      6

所以根据该变量地址可看出在内存中其存储为:0xFFFFFFFF6

0x表示十六进制,进制只是表示数据的一种方式而已,不同进制下其存储内容也不一样,但表示的值是一样的。

为什么数值在内存中统一以补码形式存储和表示呢?原因是使用补码,可以将符号位与数值域统一处理。

CPU中只有加法器,当实现减法(即正数+负数时),如果都用源码相加,得到的结果不一定正确,但是使用源码,根据相关运算规则可以得到正确的数据。

当强行将某类型范围之外的数值赋给某类型的变量时,就会出现原反补的运算,如果数值为负时,用%u打印出时忽略符号位,直接将补码当做原码转化为10进制打印出数据。而%d会考虑到符号位。

概念:大小端

大小端字节序指的是数据在电脑上存储的字节顺序

大端存储模式:数据的低位保存在内存的高地址处,而高位保存在内存的低地址处

小端存储模式:数据的低位保存在内存的低地址处,而高位保存在内存的高地址处

为了方便就记小端为大对大小对小。

为什么会出现大小端呢?对于位数大于8位的处理器,寄存器宽度大于一个字节,就必然存在如何安排多个字节存放的问题。大小端就是为了解决这种问题产生的。

数据的低位高位指的是其二进制序列中某部分相对于序列整体处于高位还是低位,而内存中的低地址与高地址请参考数据从低到高放入数据(压栈),从低到高取出数据的思维图。

如何判断该机器是大端还是小端存储方式呢?我们可以创建值为1的整型,然后char*p,将整型的地址赋予p,再根据p置访问1个字节的特性,可以知道p中的值,进而判断大小端。

当char类型的变量用%d打印时会发生整型提升。

整形提升规则:如从char提升到整形-即前面补32-8=24个符号位数(1)。补完之后根据打印的是有符号还是无符号数据进行原反补转化,有符号位补符号位,无符号位补0。

对于char c来说也要整形提升,但c为无符号变量(之前声明过),所以提升规则为高位直接补0,之后规则同上。

由于printf是可变参数的函数,所以后面参数的类型是未知的,所以甭管你传入的是什么类型,printf只会根据类型的不同将用两种不同的长度存储。其中8字节的只有long long、float和double(注意float会处理成double再传入),其他类型都是4字节。所以虽然a + b的类型是char,实际接收时还是用一个四字节整数接收的。另外,读取时,%lld、%llx等整型方式和%f、%lf等浮点型方式读8字节,其他读4字节。

unsigned与sign或者其他修饰符修饰的变量的都有范围,其范围类似一个循环,当数超出了他的范围就会进行减法如:unsigned char a=300;a=300-256=44;

其取值范围是一个圆形循环,不管存入什么值,最后都会像%最大值一样控制值在范围内。

若在char a中存入128时,虽然最高位为符号位,但截断存储时不管那么多直接按照整数的二进制序列对应大小端部分取内容,char是有符号位,所以前面补符号位1,再按%u打印

函数sleep(数值),()中的数据以ms为单位,该语句的作用为让程序在此暂停多少毫秒,需要搭配#include使用。

浮点型:

float

double

long double

头文件float.h定义了浮点型数的取值范围,limits.h定义了整形数的取值范围

float指针访问的空间为4个字节(float与int类型所占空间大小一样)。但将整形数据以float形式拿出(或反之),得到的数据不一样了,说明float与int类型数据存储方式不一样。

浮点数储存规则:
任意一个二进制浮点数可以写成这种形式:(-1)^S*M*2^E

(-1)^S表示符号位,S取值为0或1。M为有效数字,取值在1到2之间,2^E表示指数位。

例如十进制的5.0可以写成二进制序列101.0,需要变为1.01*(2^2)(前面的2表示进制,后面的2表示小数点前进的个数),为正数,则S为0,M为1.01,E为2

二进制数如:101.101中小数点后面的计数为小数点后一位为2的-1次方,小数点后二位为2的-2次方等等类推,像3.14这种数很难精确保存,需要很多空间,但所有的float数据都可以写成(-1)^S*M*E...的形式。所以若要进行小数比较,不能直接使用==进行比较,因为如0.00001此种小数数据并不能精确存储。

解决方法:如f-0<=一个被允许的最大误差范围数。此数可以使用数学函数等多种方法进行表示。

单精度浮点数储存时需要4个字节也就是32个比特位,标准规定:最高第一位用来储存S的值(0/1),之后的八位用来储存E,之后全部位数用来储存M。

而对于双精度来说,第一个bit为储存S,之后11位储存E,之后52个bit位储存M。

M的储存规则:M由于是小数而整数部分始终为1,所以储存时整数的1省略,只记录小数点后面的数,等到取出时再加上1.

E的储存规则:由于E始终为一个无符号整数,若为8位则其数值范围为0-255,若为11为则其数值范围为0-2047,但是科学计数法中的E可以表示为负数(如0.5=5*(10^-1))。所以储存时,E需要加上一个中间数,8位时中间数为127,11位时中间数为1023,再变为二进制储存。

举例:101.1转换成为(-1)^S*M*E

S=0,M=1.011,2^2中后面的2为E(小数点向前进的位数),前面的2表示进制

0 10000001(2+127(中间数)) 011 (小数点后的数,1被省略了)0000 00000000000000(补上的)

第一位为S,后面是E,之后为M,口诀SEM。

将这些码组合起来,根据字节换成数据可以在调试窗口中查找地址查看数据。

修正规则:E需要加上中间数(8bit位中间数位127,11位为1023)实现当数值为负数的存储。

E取出时有三种情况:
1.E中序列不全为0或不全为1

规则:让E中序列转换为十进制,再减去中间数后再回到二进制作为补码的一部分储存,M部分则加上1.

2.E中序列全为0

这时浮点数中E的序列表示真实的E(可以直接使用),而M不用加上1,而是还原为0.。。。。。。的小数,这样做的意义为了表示这个被储存的数值很小,无限接近于0,表示为正负0.

3.E中序列全为1

这时的E也为真实值,如果M序列中所有的数全为0,则这个被储存的原数表示为无穷大,正负看S。

构造类型:

数组类型:如int arr[10];中arr的类型为int [10]。

结构体类型(struct)

枚举类型(enum)

联合类型(union)

数组类型:

int* pi;

char* pc;

float* pf;

void* pv;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值