目录
一、整数在内存中的储存
在刚接触c语言的时候,我们就知道了整数是以二进制存储在内存中。而整数的二进制表示方法又有三种,即原码,反码和补码。有符号的整数中,这三种表示方法均会分为符号位和数值位两个部分。
符号位是最高位,剩余的都是数值位。符号位用来表示该整数的正负,当符号位为0,则表示该整数为“正”,当符号位为1,则表示该整数为“负”。
正数的原、反、补码都是相同的。
而负数的原、反、补码的表示方式都不一样。
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位次依次按位取反就得到反码。
例:-111的原码——1110 1111,-111的反码——1001 0000
补码:反码+1就得到补码。
例:-111的反码——1001 0000,-111的补码——1001 0001
注:虽然正数的原反补码的表示形式都是相同的,但其实内存中存放的是正数的补码。因为使用补码可以将符号位和数值域统一处理。
二、大小端字节序以及字节序的判断
在我们了解整数在内存中的储存之后,我们进行以下代码的调试观察:
#include<stdio.h>
int main()
{
int a = 0x11223344;
//0x表示用16进制表示
return 0;
}
查看a地址中存放的数据,我们发现a中的0x11223344是以字节为单位存储,而存储顺序与我们赋值给a的数据正好为相反,这是为什么呢?由此我们引出大小端的概念。
1.什么是大小端?
当存储的数据超过一个字节的时候,就会产生存储顺序的问题,按照不同的存储顺序,我们将其分为大端字节序存储和小端字节序存储。
大端存储:指数据的低位字节的内容保存在内存的高地址处,而数据的高位字节的内容,则保存在内存的低地址处。
小端存储:指数据的低位字节的内容保存在内存的低地址处,而数据的高位字节的内容,则保存在内存的高地址处。
例:大端储存:int a=0x11223344; 内存中的存储顺序为:11 22 33 44
小段储存:int a=0x11223344; 内存中的存储顺序为:44 33 22 11
由此可以推断出我当前使用的vs编译器中使用的小端储存,当然我们也可以选择更通俗的方式去判断大小端存储,即:正着存放则为大端,倒着存放则为小端。
2.大小端产生的原因
在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit位,但是在C语⾔中除了8bit的 char 之外,还有16bit的 short 型,32bit的 long 型,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度⼤于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。
3.用代码测试当前编译器采用什么存储方式
当我们了解了大小端存储的概念之后,我们可以尝试去编写代码去判断自己使用的编译器采用什么存储方式。
我们可以以1作为测试大小端的关键。
1的补码——00000000 00000000 00000000 0000001
即1的存储形式无外乎两种①00 00 00 01 ②01 00 00 00
此时只要返回第一个字节的内容就可以直到编译器是如何存储的,代码如下
#include <stdio.h>
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;
}
运行结果为“小端”,我们来进行分析:
这是进行调试时i地址上存放的数据。
i中存放整型1,i在内存中的存放顺序如上图所示。该行代码将i的地址强制转为字符型地址,此时进行解引用,ret得到的返回值则为前一个字节的内容,即该位置(红色圈圈)的内容,为1,则为小端存储。
如果该编译器为大端存储,则内存存储形式应该为00 00 00 01,此时返回前一个字节即红圈圈中的内容,很明显,返回值为0,则判断为大端储存。
此外,我们也可以对以上代码进行优化
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
相比我们之前用字符类型直接强转,我们可以利用联合体共用一块空间的特性,直接返回char类型的c,更加方便。