数据在内存中的存储
- 1. 整数在内存中的存储
- 2. 大小端字节序和字节序判断
- 2.1 什么是大小端?
- 2.2 为什么有大小端?
- 2.3 练习
- 2.3.1 练习1
- 2.3.2 练习2
- 2.3.3 练习3
- 第一题
- 第二题
- 2.3.4 练习4
- 2.3.5 练习5
- 第一题
- 第二题
- 2.3.6 练习6
1. 整数在内存中的存储
在讲解操作符的时候,我们就讲过了下⾯的内容:有符号的整数的2进制表⽰⽅法有三种,即 原码、反码和补码三种表示方法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,⽽数值位最⾼位的⼀位是被当做符号位,剩余的都是数值位。
正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
#include <stdio.h>
int main()
{int a = 20;//00000000000000000000000000010100 -- 源码//00000000000000000000000000010100 -- 反码//0000 0000 0000 0000 0000 0000 0001 0100 -- 补码// 0 0 0 0 0 0 1 4 -- 16进制表示//0x00 00 00 14int b = -10;//10000000000000000000000000001010 -- 源码//0x80 00 00 0a//11111111111111111111111111110101 -- 反码//0xff ff ff f5//11111111111111111111111111110110 -- 补码//0xff ff ff f6return 0;
}
所以,对于整形来说:数据存放内存中其实存放的是补码。
为什么呢?
在计算机系统中,数值⼀律⽤补码来表⽰和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
这些内容在在以前的博客中详细讲过了,点击此处即可查看
2. 大小端字节序和字节序判断
当我们了解了整数在内存中存储后,我们调试看⼀个细节:
#include <stdio.h>
int main()
{int a = 0x11223344;return 0;
}
调试的时候,我们可以看到在a中的 0x11223344 这个数字是按照字节为单位,倒着存储的。这是为什么呢?
2.1 什么是大小端?
其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为⼤端字节序存储和⼩端字节序存储,下⾯是具体的概念:
大端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,而数据的高位字节内容,保存在内存的低地址处。
小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。
上述概念需要记住,方便分辨大小端。
画图演示:
2.2 为什么有大小端?
为什么会有大小端模式之分呢?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit 位,但是在C语⾔中除了8 bit 的char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看具体的编译器),另外,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。
例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为0x1122 ,那么 0x11 为⾼字节,0x22 为低字节。对于⼤端模式,就将 0x11 放在低地址中,即0x0010 中,0x22 放在⾼地址中,即 0x11 中。⼩端模式,刚好相反。我们常⽤的 X86 结构是⼩端模式,⽽ KEIL C51 则为⼤端模式。很多的ARM,DSP都为⼩端模式。有些ARM处理器还可以由硬件来选择是⼤端模式还是⼩端模式。
2.3 练习
2.3.1 练习1
请简述⼤端字节序和⼩端字节序的概念,设计⼀个⼩程序来判断当前机器的字节序。(10分)- 百度笔试题
#include <stdio.h>int check_sys()
{int a = 1;//1. 取出a的地址//2. 强制类型转换成char*后解引用,只取第一个字节的数据//3. 如果取出的是1,就是小端,取出的是0就是大端return *(char*)&a;
}int main()
{int a = 1;//0x00 00 00 01int ret = check_sys();if (ret == 1){printf("小端\n");}else{printf("小端\n");}return 0;
}
运行结果如图:
2.3.2 练习2
#include <stdio.h>
int main()
{char a = -1;//10000000000000000000000000000001//11111111111111111111111111111110//11111111111111111111111111111111//存储在a中要发生截断//11111111 - asigned char b = -1;//11111111 - bunsigned char c = -1;//11111111 - c//00000000000000000000000011111111printf("a=%d,b=%d,c=%d", a, b, c);//%d - 十进制的形式打印有符号的整数//这里会发生整形提升,无符号数整形提升直接补0//return 0;
}
运行结果如图:
2.3.3 练习3
第一题
#include <stdio.h>
int main()
{char a = -128;//10000000000000000000000010000000//11111111111111111111111101111111//11111111111111111111111110000000//10000000 - a//打印时发生整形提升,有符号数按符号位提升//11111111111111111111111110000000//signed char取值范围: -128~127//unsigned char取值范围: 0~255printf("%u\n", a);//%u 是以十进制的形式打印无符号的整数return 0;
}
打开计算器可以观察到:
运行结果如图:
第二题
#include <stdio.h>
int main()
{char a = 128;//00000000000000000000000010000000//10000000 - a//打印时发生整形提升,有符号数按符号位提升//11111111111111111111111110000000printf("%u\n", a);return 0;
}
这里可以观察到和上一题一样
运行结果如图:
2.3.4 练习4
#include <stdio.h>
int main()
{char a[1000];int i;for (i = 0; i < 1000; i++){a[i] = -1 - i;}//arr[i] --> char -128~127//-1 -2 -3 -4 ... -1000//-1 -2 -3 ... -128 127 126 125 ... 5 4 3 2 1 0 -1 ...//128 + 127 = 255printf("%d", strlen(a));return 0;
}
运行结果如图:
2.3.5 练习5
第一题
#include <stdio.h>
unsigned char i = 0;
//0~255
//当i等于255时
//00000000000000000000000011111111 - i(255)
//00000000000000000000000000000001 - 1
//00000000000000000000000100000000 - i + 1
//00000000 - i + 1 由于char只占8个比特位,所以发生截断
int main()
{//死循环打印"hello world"for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;
}
第二题
#include <stdio.h>
#include <Windows.h>
int main()
{unsigned int i;//当i等于0时//00000000000000000000000000000000 - i(0)//10000000000000000000000000000001 - -1的原码//11111111111111111111111111111110 - -1的反码//11111111111111111111111111111111 - -1的补码//00000000000000000000000000000000 - i//11111111111111111111111111111111 - i - 1 for (i = 9; i >= 0; i--){//死循环打印printf("%u\n", i);Sleep(1000);}return 0;
}
运行结果如图:
打开计算器可以看到
2.3.6 练习6
#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;
}
运行结果如图:
画图分析:
由于,vs2022采用的是小段存储,所以,存放数据的时候低字节存放在地址处高字节存放在高地址处,故取出来时高位在右边低位在左边
通过监视也可以观察到
一个字节一个字节的显示情况如图