1. 整数在内存中的存储
整数的2进制表示方法有三种 ,即 原码、反码和补码
有符号类型数据三种表示方法均有符号位和数值位两部分 ,符号位都是用0表示“正” ,用1表示“负” ,最高位的一位是被当做符号位 ,剩余的都是数值位。
无符号类型的数据没有符号位只能表示正数。
正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变 ,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
对于整形来说:数据存放内存中其实存放的是补码。
为什么呢?
在计算机系统中 ,数值一律用补码来表示和存储。原因在于 ,使用补码 ,可以将符号位和数值域统一处理;同时 ,加法和减法也可以统一处理(CPU只有加法器) 此外 ,补码与原码相互转换 ,其运算过程是相同的 ,不需要额外的硬件电路。
2. 大小端字节序和字节序
我们来看一下数据在在内存中具体是怎么存在的:
我们发现16进制的11223344在内存中是以字节为单位倒着存储的。为什么呢?
2.1 什么是大小端
其实超过一个字节的数据在内存中存储的时候 ,就有存储顺序的问题 ,按照不同的存储顺序 ,我们分为大端字节序存储和小端字节序存储 ,下面是具体的概念:
大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处 ,而数据的高位字节内容 ,保存在内存的低地址处。
小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处 ,而数据的高位字节内容 ,保存在内存的高地址处。
2.2 为什么有大小端?
这是因为在计算机系统中 ,我们是以字节为单位的 ,每个地址单元都对应着一个字节 ,一个字节为8 bit 位 ,但是在C语言中除了8 bit 的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外 ,对于位数大于8位的处理器 ,例如16位或者32位的处理器 , 由于寄存器宽度大 于一个字节 ,那么必然存在着一个如何将多个字节安排的问题。 因此就导致了大端存储模式和小端存储模式。
例如:一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节,对于大端模式,就将0x11放在低地址中,及0x0010中,0x22放在高地址中,及0x0011中;小端模式,和其相反,我们常用的x86结构是小端模式,而KETL C51则为大端模式,很多ARM,DSP都为小端模式,有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
2.2 设计程序验证大小端
我们来设计一个程序来看看我们的编译器是什么模式
#include <stdio.h>
int Check()
{
int i = 1;
char* p = &i;
return *p;
//大端模式下返回0
//小端模式下返回1
}
int main()
{
if (Check())
printf("小端模式");
else
printf("大端模式");
return 0;
}
我在vs2022下运行结果为:小端模式
练习题
我们再来看一道练习题
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
这道题的输出结果是什么,先想一想。
解析:先看ptr1,&a+1指针跳过整个数组,然后有强制类型转换为int*,ptr1[-1]等于*(ptr-1),ptr-1,指向数组中的4,然后解引用结果为4,
再看ptr2,a为数组的首元素地址,将其强制类型转换为int再+1,即为地址这个数字加了1,再强制类型转换换位int*及指针指向了数组元素1中的第二个字节,然后再解引用,向后访问四个字节,因为我的编译器是小端模式,1在内存中是0x01000000 .2在内存中是这样的0x04000000,ptr2解引用访问到元素1中的0x000000和元素2中的0x02即返回0x02000000,以16进制打印结果为2000000
故答案为 4,2000000
感谢观看,欢迎在评论区讨论。