数据表示与定义
本章介绍 x86 汇编中的数据表示方式,包括数据类型、字节序、数据定义伪指令等内容。
数据类型
基本数据类型
x86 架构支持以下基本数据类型:
| 类型 | 大小 | NASM 伪指令 | 范围 |
|---|---|---|---|
| 字节 | 8 位 | db / resb | 0 |
| 字 | 16 位 | dw / resw | 0 |
| 双字 | 32 位 | dd / resd | 约 ±2.1×10⁹ |
| 四字 | 64 位 | dq / resq | 约 ±9.2×10¹⁸ |
| 十字节 | 80 位 | dt / rest | 用于浮点数 |
有符号与无符号
x86 指令本身不区分有符号和无符号,区别在于如何解释结果和使用哪些标志位:
mov al, -1 ; AL = 0xFF
; 无符号解释
movzx ax, al ; AX = 255
; 有符号解释
movsx ax, al ; AX = -1 (0xFFFF)
整数表示
无符号整数
直接使用二进制表示,所有位都用于表示数值:
8 位无符号:0 ~ 255
16 位无符号:0 ~ 65,535
32 位无符号:0 ~ 4,294,967,295
64 位无符号:0 ~ 18,446,744,073,709,551,615
有符号整数(补码)
最高位为符号位,0 表示正数,1 表示负数:
正数:直接二进制表示
负数:绝对值取反加一
例如:-5(8位)
5 = 00000101
取反 = 11111010
加一 = 11111011 = 0xFB
mov al, -5 ; AL = 0xFB
mov al, 251 ; AL = 0xFB(相同的位模式)
; 有符号比较
cmp al, 0
jl negative ; 如果 AL < 0(有符号),跳转
; 无符号比较
cmp al, 0
jb negative ; 如果 AL < 0(无符号),跳转
字节序
大端序与小端序
x86 架构使用小端序(Little Endian),即低位字节存储在低地址。
示例:存储 0x12345678
地址 大端序 小端序(x86)
0x1000 0x12 0x78
0x1001 0x34 0x56
0x1002 0x56 0x34
0x1003 0x78 0x12
代码示例
section .data
value dd 0x12345678 ; 32 位整数
section .text
mov eax, [value] ; EAX = 0x12345678
; 访问单个字节
mov al, [value] ; AL = 0x78(最低字节)
mov al, [value+1] ; AL = 0x56
mov al, [value+2] ; AL = 0x34
mov al, [value+3] ; AL = 0x12(最高字节)
字节序转换
在网络编程中,经常需要进行字节序转换:
; 16 位字节序转换(网络字节序是大端)
; 输入:AX = 0x1234
; 输出:AX = 0x3412
swap16:
mov cx, ax
xchg cl, ch
mov ax, cx
ret
; 32 位字节序转换
; 输入:EAX
; 输出:EAX(字节序反转)
swap32:
bswap eax
ret
数据定义伪指令
初始化数据
使用 db、dw、dd、dq 定义已初始化的数据:
section .data
; 字节
byte_val db 0x12 ; 单个字节
byte_arr db 1, 2, 3, 4 ; 字节数组
char_val db 'A' ; 字符
string_val db 'Hello', 0 ; 字符串(以 null 结尾)
; 字(16 位)
word_val dw 0x1234 ; 单个字
word_arr dw 1, 2, 3, 4 ; 字数组
; 双字(32 位)
dword_val dd 0x12345678 ; 单个双字
ptr_val dd some_label ; 地址(32 位)
; 四字(64 位)
qword_val dq 0x123456789ABCDEF0
ptr64_val dq some_label ; 地址(64 位)
; 浮点数
float_val dd 3.14159 ; 单精度浮点
double_val dq 3.14159265358979 ; 双精度浮点
未初始化数据
使用 resb、resw、resd、resq 保留未初始化的空间:
section .bss
buffer resb 1024 ; 保留 1024 字节
array resd 100 ; 保留 100 个双字(400 字节)
stack resq 256 ; 保留 256 个四字(2048 字节)
常量定义
使用 equ 定义常量:
section .data
buffer_size equ 1024
max_count equ 100
buffer resb buffer_size
array resd max_count
; 计算字符串长度
message db 'Hello, World!', 0
msg_len equ $ - message ; $ 是当前地址
重复填充
使用 times 重复定义:
section .data
; 填充 256 个 0
zeros times 256 db 0
; 填充 100 个 0xFF
ones times 100 db 0xFF
; 填充 50 个字 0x1234
words times 50 dw 0x1234
数组与字符串
数组定义
section .data
; 整数数组
int_array dd 10, 20, 30, 40, 50
array_len equ 5
; 字节数组
byte_array db 1, 2, 3, 4, 5, 6, 7, 8
; 二维数组(3x4 矩阵)
matrix dd 1, 2, 3, 4
dd 5, 6, 7, 8
dd 9, 10, 11, 12
数组访问
; 访问数组元素
mov eax, [int_array] ; 第 0 个元素
mov eax, [int_array + 4] ; 第 1 个元素(每个元素 4 字节)
mov eax, [int_array + 8] ; 第 2 个元素
; 使用索引访问
mov rbx, int_array ; 数组基址
mov rcx, 2 ; 索引
mov eax, [rbx + rcx*4] ; 第 2 个元素(比例因子 4)
; 遍历数组
mov rsi, int_array
mov rcx, array_len
xor eax, eax ; 累加器
sum_loop:
add eax, [rsi]
add rsi, 4 ; 指针移动
loop sum_loop
字符串定义
section .data
; 以 null 结尾的字符串(C 风格)
str1 db 'Hello, World!', 0
; 带长度的字符串
str2 db 'Hello'
str2_len equ $ - str2
; 多行字符串
multiline db 'Line 1', 0xA
db 'Line 2', 0xA
db 'Line 3', 0
; 转义字符
escapes db 'Tab:', 0x09, 'Newline:', 0x0A, 0
字符串长度计算
; 计算以 null 结尾的字符串长度
; 输入:RSI = 字符串地址
; 输出:RCX = 长度
strlen:
xor ecx, ecx ; 计数器清零
strlen_loop:
cmp byte [rsi + rcx], 0
je strlen_done
inc ecx
jmp strlen_loop
strlen_done:
ret
结构体
NASM 支持 struc 伪指令定义结构体:
; 定义结构体
struc Point
.x resd 1
.y resd 1
endstruc
struc Person
.name resb 32
.age resd 1
.id resq 1
endstruc
section .data
; 定义结构体实例
point1:
istruc Point
at Point.x, dd 10
at Point.y, dd 20
iend
person1:
istruc Person
at Person.name, db 'Alice', 0
at Person.age, dd 25
at Person.id, dq 1001
iend
section .bss
; 未初始化的结构体
point2 resb Point_size
person2 resb Person_size
结构体访问
; 访问结构体成员
mov eax, [point1 + Point.x]
mov ebx, [point1 + Point.y]
; 修改结构体成员
mov dword [point1 + Point.x], 100
mov dword [point1 + Point.y], 200
; 结构体数组
section .data
points:
dd 0, 0 ; Point 0
dd 10, 10 ; Point 1
dd 20, 20 ; Point 2
; 访问第 i 个点的 x 坐标
mov rax, i
imul rax, Point_size
mov ebx, [points + rax + Point.x]
对齐
数据对齐的重要性
x86 处理器支持非对齐访问,但对齐访问性能更好:
; 对齐访问(推荐)
section .data
align 4
aligned_dword dd 0x12345678
; 非对齐访问(性能较差)
section .data
unaligned_byte db 0
unaligned_dword dd 0x12345678 ; 可能非 4 字节对齐
对齐伪指令
section .data
byte1 db 0x12
align 4 ; 4 字节对齐
dword1 dd 0x12345678
align 16 ; 16 字节对齐(SSE 数据常用)
sse_data dd 1, 2, 3, 4
align 32 ; 32 字节对齐(AVX 数据常用)
avx_data dd 1, 2, 3, 4, 5, 6, 7, 8
数据类型转换
零扩展
; movzx:零扩展传送
movzx eax, al ; 8 位 -> 32 位,高位填 0
movzx eax, ax ; 16 位 -> 32 位,高位填 0
movzx rax, byte [rsi] ; 8 位 -> 64 位
符号扩展
; movsx:符号扩展传送
movsx eax, al ; 8 位 -> 32 位,高位填符号位
movsx eax, ax ; 16 位 -> 32 位
movsx rax, byte [rsi] ; 8 位 -> 64 位
; cbw、cwde、cdqe:累加器符号扩展
cbw ; AL -> AX(符号扩展)
cwde ; AX -> EAX(符号扩展)
cdqe ; EAX -> RAX(符号扩展)
符号扩展示例
mov al, -5 ; AL = 0xFB
movsx ax, al ; AX = 0xFFFB = -5
movsx eax, al ; EAX = 0xFFFFFFFB = -5
mov al, 100 ; AL = 0x64
movzx ax, al ; AX = 0x0064 = 100
小结
本章介绍了 x86 汇编中的数据表示:
- 数据类型:字节、字、双字、四字等
- 字节序:x86 使用小端序
- 数据定义:db、dw、dd、dq 等伪指令
- 数组与字符串:定义和访问方法
- 结构体:使用 struc 定义结构体
- 对齐:数据对齐的重要性
下一章将学习 x86 的内存寻址模式。