计算机中的数据存储与运算
输出地址后看不懂格式,为什么?
第一节:进制转换基础
✅ 常见进制:
- 十进制(Decimal):日常使用的 0~9
- 二进制(Binary):计算机底层使用,仅有 0 和 1
- 八进制(Octal):0~7,早期用于嵌入式
- 十六进制(Hex):09A~F(代表 0~15),常用于表示地址,为二进制的缩写等
✅ 为什么使用十六进制表示二进制?
- 二进制太长不直观,容易出错
- 1 个十六进制位 = 4 个二进制位(更简洁)
- 举例:
11111111 11111111
可以写成0xFFFF
- 程序员常用
0x
开头表示十六进制,方便调试与内存分析
✅ 转换示例:
十进制 | 二进制 | 十六进制 |
---|---|---|
10 | 1010 | 0xA |
255 | 11111111 | 0xFF |
16 | 10000 | 0x10 |
第二节:数据存储方式
✅ 什么是“位数”?
-
位(bit):计算机的最小单位,0 或 1
-
32 位系统:一个寄存器/地址一次最多处理 32 位数据,11111111 11111111 11111111 11111111 ,0xFFFFFFFF
-
64 位系统:一次最多处理 64 位数据,以上例子的两倍
-
位数决定:寻址能力、运算精度、可用内存(每个地址能存一个字节的内容)等
✅ 什么是“字节”?
- 一字节等于8位,2^8,能存2的8次方大小的数据
✅ 常见存储范围
8 位 | 2⁸ = 256 | 256 个值 | 0 ~ 255 或 -128 ~ 127 |
---|---|---|---|
32 位 | 2³² ≈ 42 亿 | 42 亿个值 | 0 ~ 4294967295 或 -2147483648 ~ 2147483647 |
16位 65536 -32768~32767
✅ 整型存储:
-
占用固定字节数(如
int
通常 4 字节 = 32 位) -
按照补码方式存储(后面详细讲)
-
二进制按位存储,例如:
int a = 5; // 实际存储:00000000 00000000 00000000 00000101 32位系统下地址:0x00 00 00 05 64位同理
✅ 浮点型存储:(极为复杂不做了解)
- 拆成三部分:符号位 + 指数位 + 尾数位(有效数字)
- 单精度(float):32 位 = 1 + 8 + 23
- 双精度(double):64 位 = 1 + 11 + 52
✅ 单精度 vs 双精度区别:(只需要知道是双倍精度,存储空间是双倍就行了)
属性 | float(单精度) | double(双精度) |
---|---|---|
位数 | 32 位 | 64 位 |
精度 | 约 7 位十进制 | 约 15 位十进制 |
范围 | ±3.4e38 | ±1.8e308 |
占用内存 | 4 字节 | 8 字节 |
✅ 最通俗的一句话总结:(也不用学)
在浮点数中:
- 尾数位 决定 “是什么数” (有效数字)
- 指数位 决定 “它放在哪个大小范围” (缩放倍数)
字符型存储与 ASCII
✅ 字符 = 整数
- 在底层,
char
类型是 1 字节整数 - 字符用整数存储,那么为什么知道是字符呢?对应表格
- 字符
'A'
实际存储值为 65,对应 ASCII 编码表,0100 0001,0x41
✅ ASCII 表示例:
字符 | 十进制 | 二进制 |
---|---|---|
'A' | 65 | 01000001 |
'a' | 97 | 01100001 |
'0' | 48 | 00110000 |
✅ 为什么A和a差了32?
0x20
只需要记住大写A是65就记住全部
✅ 演示代码:
char c = 'A';
cout << (int)c << endl; // 输出 65
cout << (int)(c + 0x20) << endl;
cout << (c += 0x20) << endl;
cout << (c -= 0x20) << endl;
✅ 说明:字符本质上就是整数,赋值/比较时可作为数值使用。
ASCII码不过一个字节大小范围,0~255,如何记得下全世界数百上千万的符号?
不同的语言体系有不同的编码版本,
✅ 扩展:其他编码(中文/Unicode)
编码方式 | 特点 | 示例(字符“你”) |
---|---|---|
ASCII | 单字节,只支持英文 | 不支持 |
GB2312/GBK | 汉字双字节编码 | 0xC4E3 |
UTF-8 | 可变长度编码,全球通用 | 0xE4 0xBD 0xA0 (3 字节) |
Unicode/UTF-16 | 固定宽度(2 字节) | 0x4F60 |
十六进制序列 | Windows-1252 | GBK | UTF-8 |
---|---|---|---|
0xBDB2 | ½ (分数 1⁄2) | 汉字 “貢” | 非法² |
✅ 总结
- 数据在内存中以 二进制补码 的形式存储
- 字符、整数、浮点数都有不同的存储结构
- 编码方式影响符号处理,补码是主流方式
- ASCII 编码将字符与整数绑定,字符可以看作数
- 二进制运算是低级优化与嵌入式开发的基础
- UTF-8/Unicode 等是现代字符集标准,支持多语言
- 计算机内存是一个大快递仓库,每个字节有唯一编号(地址)
- 数据在内存中必须分配“空间”(多少字节)
- 不同类型变量占用空间不同,越大越精确,但浪费空间
第三节:编码方式(原码、反码、补码)
(为何要用补码,原理较为复杂,不要求掌握,只要求掌握计算补码)
计算机中负数存的不是原码,而是补码,便于计算机运算,(为什么)
✅ 有符号数(负数):
- 最高位为符号位:0 代表正,1代表负,以四位数据的原码举例,1为0001,-1为1001
- 正数用原码存储,讨论补码时,都是讨论负数
✅ 编码定义:
编码方式 | 定义 |
---|---|
原码 | 符号位 + 绝对值(除去符号的值) |
反码 | 原码除符号位外取反 |
补码 | 反码 + 1 |
总结就是,补码就是正数原码取反+1
-5的补码计算 -> 抛开第一位符号位 -> 5的二进制原码 -> 0101 -> 取反 -> 1010 -> +1 -> 1011
5的原码是0101,-5的补码为1011
✅ 原因、原理:
芯片底层进行加减法运算的时候,只使用加法模块,单独设计减法模块太复杂,那么减法用加法模块替代,如何替代?
以四位长度数据计算举例:5 - 1 = 5 + (2^4 - 1) - 2^4
1.此处2^4是一个4位长度的数据的最大范围 + 1
二进制表示为:0101 + (10000 - 0001)- 10000 = 0101 + 1111 - 10000
2.此处的1111也就是(10000 - 0001)的计算结果,但其实1111是0001取反+1的结果,而非真正执行了减法,意思就是用取反模块和加法模块在此处模拟出了减法模块,但此处还没有结束,我们继续计算,10100 - 10000,这一步又要如何减去呢?
3.前面提到过,这是一个4位长度的数据,所以会自动把最高的那一位给丢弃,也就达成了减去最高位的1的效果,最后的结果也就是0100也就是4,5 - 1 = 4,就完成了,全程没有使用减法模块
4.这便是计算补码的原因以及原理,所以负数都以补码的形式存储,运算的时候使用的是加法模块,原码和补码也就是通过取反+1互相转换,1的原码为0001,补码为1111,(10000 - 0001) = 1111,(10000 - 1111) = 0001
✅ 示例:以 8 位为例,表示 -5:
编码方式 | 二进制表达 |
---|---|
原码 | 10000101 |
反码 | 11111010 |
补码 | 11111011 ✅ |
✅ 无符号数 unsigned 前缀 unsigned int 无符号整型 unsigned double 无符号浮点型
- 全部位用于表示数值,不能表示负数
- 例如 8 位无符号最大值为 255,0 ~ 255, 二进制 0000 0000 ~ 1111 1111
- 带符号最大值为 127,最小为-128,也就是 0 ~ 127,-1 ~ -128
- 二进制 0000 0000 ~ 0111 1111, 原码1000 0001 ~ 1111 1111,补码 1111 1111 ~ 1000 0001
第四节:二进制运算基础
✅ 常见运算符:
运算符 | 含义 | 示例(a = 5, b = 3)a = 0101, b = 0011 |
---|---|---|
& | 按位与 | a & b = 1 |
| | 按位或 | a | b = 7 |
^ | 按位异或 | a ^ b = 6 |
~ | 取反 | ~a = -6 |
<< | 左移 | a << 1 = 10 |
>> | 右移 | a >> 1 = 2 |
✅ 十进制类比:
运算符 | 类似什么 | 示例 |
---|---|---|
& | 相当于“都满足” | 0101 & 0011 = 0001 |
| | 相当于“至少一个满足” | 0101 | 0011 = 0111 |
^ | 相当于“不同为真” | 0101 ^ 0011 = 0110 |
~ | 相当于“取反” | ~00000101 = 11111010 |
<< | 乘 2^n | 101 << 1 = 1010 |
>> | 除 2^n | 101 >> 1 = 2 |
✅ 运算例题:
7 & 3 = 0111 & 0011 = 0011 = 3
4 | 2 = 0100 | 0010 = 0110 = 6
5 ^ 1 = 0101 ^ 0001 = 0100 = 4
~1 = 11111110
(此处取反后为补码,补码无法一眼看出大小,通过反码+1还原成原码,可以看出实际大小)2 << 2 = 8
,8 >> 1 = 4
✅ 位运算和普通数学运算之间的关系概览
位运算符 | 数学意义 | 示例 | 对应数学操作 |
---|---|---|---|
<< n | 左移 n 位 | a << n = a × 2ⁿ | 快速乘法(×2ⁿ) |
>> n | 右移 n 位 | a >> n = a ÷ 2ⁿ (整数除法) | 快速除法(÷2ⁿ,舍去余数) |
^ | 按位异或(相同为0) | a ^ b | 二进制的“无进位加法” |
~ | 按位取反 | ~a | 与 -a - 1 等价 |
✅ 一、<<
左移:等于乘以 2 的 n 次方
a << n ≡ a × 2ⁿ
示例:
3 << 2 = 3 × 4 = 12
二进制表示:
3 = 00000011
左移 2 位 → 00001100 = 12
✅ 二、>>
右移:等于除以 2 的 n 次方(向下取整)
a >> n ≡ a ÷ 2ⁿ(向下取整)
示例:
19 >> 2 = 19 ÷ 4 = 4 (舍去余数)
✅ 三、^
按位异或:相同为 0,不同为 1(无进位加法)
a = 1101
b = 1011
→ a ^ b = 0110 此处第一位和第四位没有进位
🔁 **异或的一大作用:**用于交换两个数
a ^ a = 0
a ^ 0 = a
常用于:
- 交换两个数不用临时变量:(原理,能理解最好,但并不常用到,可以自己推一遍)
a ^= b;
b ^= a;
a ^= b;
a = 1101
b = 1011
a ^= b; a 1101 b 1011 -> a 0110
b ^= a; a 0110 b 1011 -> b 1101
a ^= b; a 0110 b 1101 -> a 1011
A 1101
B 1011
C 0110
标记计算法:C就是A和B异或计算得出的标记位,每一位的0或1都是一个标记
不同为0,相同为1,与0异或维持不变,与1异或反转自身
C第一、四位的0表示,AB的第一、四位相同,无论是1还是0,异或运算0以后还是自身
C第二、三位的1表示,AB的第二、三位不同,无论是1还是0,异或运算1以后都要反转,因为位不同,反转之后只能是对方的位
🧠 建议:手动练习补码运算、位操作、小型进制转换程序,有助于理解底层原理。