跳到主要内容

数据表示与定义

本章介绍 x86 汇编中的数据表示方式,包括数据类型、字节序、数据定义伪指令等内容。

数据类型

基本数据类型

x86 架构支持以下基本数据类型:

类型大小NASM 伪指令范围
字节8 位db / resb0255 或 -128127
16 位dw / resw065535 或 -3276832767
双字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 的内存寻址模式。