signed为有符号类型,unsigned为无符号类型,一般signed默认省略。
一、整形在内存的存储
1.1.原反补码
原反补码的概念在博客数据在内存中的存储中提到过,这里再回顾一下:
-
有符号数
对于有符号数,一定要能表示该数据是正数还是负数。
所以我们一般用最高比特位来进行充当符号位。
计算机中的有符号数有三种表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同。
如果一个数据是负数,那么就要遵守下面规则进行转化:
原码:直接将二进制按照正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
补码:反码+1就得到补码。
如果一个数据是正数,那么它的原反补都相同。 -
无符号数
无符号数:不需要转化,也不需要符号位,原反补相同。
对于整形来说:数据在内存中其实存放的是补码。
补码转为原码有两种方式:
方法一:先-1,符号位不变,其他位按位取反。
方法二:将原码到补码的过程在来一遍,即先符号位不变,其他位按位取反然后+1。
二进制快速转化为十进制:1后面跟n个比特位,就是2的n次方,然后将每个位置的1换成2的n次方相加即可
1.2.将负数赋值给无符号数
将一个负数赋值给unsigned无符号数是没有问题的。
定义变量的本质是开辟空间,然后将数据放到开辟的空间里。
在将-10
放到开辟的空间时,要先将内容转化成二进制,也就是将-10
转化成补码:1111 1111 1111 1111 1111 1111 1111 0110
,在将这个补码放到开辟的空间的时候,空间是不关心这个内容是什么的,所以会直接将这个二进制补码放到开辟的内存中。
换句话说,在将数据保存在内存的时候,数据已经被转化成二进制的形式。也就是内存中存原码还是补码取决于-10
的类型,而不取决于b的类型。-10
是一个负数,所以存二进制补码。
那么这里的变量的类型unsigned int有什么用呢?或者说变量的类型有什么用呢?
变量类型决定了使用这个变量需要开辟空间的大小以及内存中的数据是如何存储;决定着如何解释内存空间里存放的二进制码,比如在内存中同样的两块4个字节的空间,存放着同样的二进制码,如果这两块内存对应的两个变量类型不一样,比如int和float,那么它们的意义也就不一样。
在取数据的时候,会先看要以什么类型取数据,然后才决定要不要看最高符号位。如果不需要,直接二进制转成十进制。如果需要,则需要转成原码,然后才能识别。
比如:
这里以%u的方式取数据不需要看符号位,直接将二进制补码1111 1111 1111 1111 1111 1111 1111 0110
转换成十进制。
以%d的方式取数据则要看符号位,二进制补码要先转化成原码,然后将原码转换成十进制。
1.3.char类型截断问题和整形提升问题
关于这个问题在博客数据在内存中的存储已经说过了,这里再提一下:
#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);
return 0;
}
char a=-1;
在放入的时候-1是一个整数,它的大小是4个字节也就是32位,1000000000000000000000000000001
它的补码为:11111111111111111111111111111111
但是a是char类型,这个类型的变量a只能存放1个字节也就是8位:11111111
同理
signed char b=-1;
signed char 类型的b只能存放8位:11111111
unsigned char c=-1;
unsigned char类型的c只能存放8位:11111111
在打印的时候要发生整形提升,a和b是有符号类型,
所以a和b补符号位,整形提升后的补码为:11111111111111111111111111111111
原码打印后的结果为-1
c是无符号型,高位不是符号位,因此整形提升高位补0,整形提升后的补码为:
0000000000000000000000011111111,由于高位是0,所以是个正数,原反补码相同,原码打印后的结果为255
所以,整形提升看的是自身的类型,和以什么方式打印是无关的。
比如这里的c,虽然以%d的形式打印,但是c的类型是unsigned char,所以整形提升的时候默认高位补0
二、大小端的问题
大端:按照字节为单位,低权值位的数据存放在高地址处,就叫做大端
小端:按照字节为单位,低权值位的数据存放在低地址处,就叫做小端。
内存是以字节为单位进行编号的。在内存中,按照字节为单位,地址有高地址和低地址之分。
数据也是以字节为单位的,这也就意味着数据也要被分成若干份,比如一个整形int的数据就要被分成四个字节。而数据本身是有高权值位和低权值之分的,比如12,1的权值就比2的权值要搞。
大小端的问题,就是把数据的低权值位放到高地址处还是低地址处的问题。不过如何存放数据,就得需要按照同等条件去内存中取数据,如果按照大端存放,又按照小端的方式取数据,这肯定会混乱的。
在计算机早期,有的计算机厂商采用大端存储,有的厂商采用小端存储。这就导致很难统一,所以大端和小端两种方式就都保留了下来。总之这是一个历史遗留问题。
对于如何区分大小端:
小端将低权值位放到低地址处,所以小端符合这样的规律:低(低权值位)低(低地址)低(小端)
如果不符合这个规律,那就是大端。