01 数据类型和变量
1 汇编语言中的数据类型#
| type | db | dw (word) | dd (double words) | dq (quadruple words) | dt (ten bytes) | 
|---|---|---|---|---|---|
| bits | 8 | 16 | 32 | 64 | 80 | 
| c type | char | short int | long int, float | long long (__int64), double | long double | 
| printf | %lld, %llx | %Lf | 
Note
- 8086 中的 word 指的是 2 bytes, 16 bits
- RISC-V 中的 word 一般指的是 4bytes, 32bits
2 例程:变量的操作#
2.1 ERROR 和正确写法的区别?#
- 一条指令中的两个参数,最多只能有一个是内存变量
- 另一个可以是寄存器或者常数
- 无法实现两个内存变量直接运算可能是因为硬件限制
2.2 变量的地址表示#
- 段地址:[偏移地址]用来表示某个地址指向的量,等价于 C 语言中的- a[3]或- *(a+3)
- 可以在 td 中看到类似 mov eax [0004]的代码,其实这里省略了ds:
2.3 Little Endian 规则#
8086 使用小端规则,所以在 td 中会发现 12345678h 变成了 78563412,也就是 LSD 78 被放在了内存地址最小的位置。
举例来说,假定 a 的地址是 1000,则 a 的值以以下格式存放:
| Addr | Little Endian | Big Endian | 
|---|---|---|
| +1000 | 0x78 | 0x12 | 
| +1001 | 0x56 | 0x34 | 
| +1002 | 0x34 | 0x56 | 
| +1003 | 0x12 | 0x78 | 
为什么小端更好
如果只要取 a 的最低字节,那么只需要访问 a 原本的地址,方便编译
注意,数据窗口中显示的是 78563412h,但是寄存器窗口中会是正常的 12345678h。
2.4 编程中的变量对齐#
- 在进行 mov eax, ddd时,由于eax是 32 位的,所以 CPU 也会从ddd的地址处开始读 4 个 byte- 但这要求 ddd确实是dq类型,否则编译器会报错
 
- 但这要求 
- 使用强制类型转换可以允许非对齐- mov al, byte ptr ddd相当于- al = *(char *)(ds:0004)
 
| 3 种 ptr 修饰 | byte ptr | word ptr | dword ptr | 
|---|---|---|---|
| bytes | 1 | 2 | 4 | 
Attention
- ptr 宽度修饰不能用于常数
- 变量在定义的时候已经声明了类型,编译器会自动加上修饰,所以不用加 但如果不是通过变量引用的方式来引用变量(即代码中没有出现变量名),还是需要加上修饰
2.5 数组和元素合并#
此时,s 是一个数组,其大小为 4 字节。可以用 word ptr 来读取 s,但要注意 Little Endian,读出的 word 是 0201h。这样读取,相当于进行了合并。
同样的,如果进行 mov eax, dword ptr abc,实际上读取了内存中连续的 {xyz, abc}。
2.5.1 特殊情况#
把 t 中各个元素逐字节按小端展开:
所以,为了取第二个元素,实际上需要取 t[2]
指针偏移操作与 C 语言的不同
- C 语言中 ptr+1,不一定真的是+1,而是位移了一个数组元素的大小
- 汇编中 ptr+1,就是位移一个字节
3 符号扩充#
将一个宽度较小的变量赋值给一个宽度较大的变量时,会发生扩充
- 零扩充:将空位用 0 补全
- 符号扩充:将空位用最高位 sign extend 补全
4 HW:思考题#
C 语言中是否有可能将数组某个元素的后一半和下一个的前一半结合在一起
| Reassembling data | |
|---|---|
| output | |
|---|---|