汇编语言 - 数组
数组是连续存放的同类型数据集合。在汇编中,数组就是内存中一段连续的空间,通过基址加偏移的方式来访问每个元素。
一维数组定义
实例
; 数组的定义方式
section .data
; 方式1:逐个列出元素
arr1 dd 10, 20, 30, 40, 50 ; 5 个双字元素的数组
; 方式2:用 dup 初始化
arr2 dd 10 dup(0) ; 10 个双字,全为 0
; 方式3:字节数组
bytes db 1, 2, 3, 4, 5, 6 ; 6 个字节
; 方式4:字符数组(字符串)
chars db 'runoob', 0 ; 7 字节
; 数组长度计算(编译时)
arr1_len equ ($ - arr1) / 4 ; 双字数组的元素个数
arr2_len equ ($ - arr2) / 4
bytes_len equ ($ - bytes) ; 字节数组,不需除法
section .bss
; 未初始化的数组
buffer resd 100 ; 预留 100 个双字的空间
section .data
; 方式1:逐个列出元素
arr1 dd 10, 20, 30, 40, 50 ; 5 个双字元素的数组
; 方式2:用 dup 初始化
arr2 dd 10 dup(0) ; 10 个双字,全为 0
; 方式3:字节数组
bytes db 1, 2, 3, 4, 5, 6 ; 6 个字节
; 方式4:字符数组(字符串)
chars db 'runoob', 0 ; 7 字节
; 数组长度计算(编译时)
arr1_len equ ($ - arr1) / 4 ; 双字数组的元素个数
arr2_len equ ($ - arr2) / 4
bytes_len equ ($ - bytes) ; 字节数组,不需除法
section .bss
; 未初始化的数组
buffer resd 100 ; 预留 100 个双字的空间
数组元素访问
访问数组元素使用 基址 + 索引 × 元素大小 的寻址方式:
实例
; 文件路径:array_access.asm
; 数组元素的读取和写入
section .data
nums dd 100, 200, 300, 400, 500 ; 5 个元素
nums_len equ ($ - nums) / 4
section .text
global _start
_start:
; 访问第 0 个元素(下标 0)
mov eax, [nums] ; eax = 100
; 访问第 2 个元素(下标 2)
mov eax, [nums + 2 * 4] ; eax = nums[2] = 300
; 2 * 4 = 8,从 nums 偏移 8 字节
; 使用寄存器作为下标
mov esi, 3 ; 下标 = 3
mov eax, [nums + esi * 4] ; eax = nums[3] = 400
; 修改数组元素
mov dword [nums + 4], 250 ; nums[1] = 250
; 数组中现在:100, 250, 300, 400, 500
; 使用 EBX 作为基址寄存器
mov ebx, nums ; ebx = 数组基址
mov eax, [ebx + 4 * 4] ; eax = nums[4] = 500
mov eax, 1
mov ebx, 0
int 0x80
; 数组元素的读取和写入
section .data
nums dd 100, 200, 300, 400, 500 ; 5 个元素
nums_len equ ($ - nums) / 4
section .text
global _start
_start:
; 访问第 0 个元素(下标 0)
mov eax, [nums] ; eax = 100
; 访问第 2 个元素(下标 2)
mov eax, [nums + 2 * 4] ; eax = nums[2] = 300
; 2 * 4 = 8,从 nums 偏移 8 字节
; 使用寄存器作为下标
mov esi, 3 ; 下标 = 3
mov eax, [nums + esi * 4] ; eax = nums[3] = 400
; 修改数组元素
mov dword [nums + 4], 250 ; nums[1] = 250
; 数组中现在:100, 250, 300, 400, 500
; 使用 EBX 作为基址寄存器
mov ebx, nums ; ebx = 数组基址
mov eax, [ebx + 4 * 4] ; eax = nums[4] = 500
mov eax, 1
mov ebx, 0
int 0x80
注意:
[nums + esi * 4]中的乘以 4 是因为每个元素是 4 字节(双字)。如果是字节数组(db),直接写成[arr + esi];字数组(dw),写成[arr + esi * 2]。
数组遍历
实例
; 文件路径:array_traverse.asm
; 遍历数组并计算总和
section .data
numbers dd 5, 10, 15, 20, 25, 30, 35, 40, 45, 50
count equ ($ - numbers) / 4 ; 元素个数
section .text
global _start
_start:
mov ecx, count ; 循环次数
mov esi, 0 ; 下标(从 0 开始)
mov eax, 0 ; 累加和
sum_loop:
add eax, [numbers + esi * 4] ; 累加数组元素
inc esi ; 下标 +1
loop sum_loop
; eax = 5+10+15+...+50 = 275
; 另一种遍历方式:使用指针
mov ecx, count
mov ebx, numbers ; ebx 指向数组起始
mov eax, 0 ; 累加和
sum_loop2:
add eax, [ebx] ; 累加当前元素
add ebx, 4 ; 指针移动到下一个元素
loop sum_loop2
; eax = 275
mov ebx, eax ; 退出码 = 累加和
mov eax, 1
int 0x80
; 遍历数组并计算总和
section .data
numbers dd 5, 10, 15, 20, 25, 30, 35, 40, 45, 50
count equ ($ - numbers) / 4 ; 元素个数
section .text
global _start
_start:
mov ecx, count ; 循环次数
mov esi, 0 ; 下标(从 0 开始)
mov eax, 0 ; 累加和
sum_loop:
add eax, [numbers + esi * 4] ; 累加数组元素
inc esi ; 下标 +1
loop sum_loop
; eax = 5+10+15+...+50 = 275
; 另一种遍历方式:使用指针
mov ecx, count
mov ebx, numbers ; ebx 指向数组起始
mov eax, 0 ; 累加和
sum_loop2:
add eax, [ebx] ; 累加当前元素
add ebx, 4 ; 指针移动到下一个元素
loop sum_loop2
; eax = 275
mov ebx, eax ; 退出码 = 累加和
mov eax, 1
int 0x80
数组查找
实例
; 文件路径:array_search.asm
; 在数组中查找指定值
section .data
data dd 12, 45, 67, 23, 89, 34, 56, 78, 90, 11
data_len equ ($ - data) / 4
target dd 23 ; 要查找的值
found_msg db 'Found at index: ', 0
found_len equ $ - found_msg
notfound_msg db 'Not found (runoob)', 0xA
notfound_len equ $ - notfound_msg
newline db 0xA
section .text
global _start
_start:
mov ecx, data_len ; 循环次数
mov esi, 0 ; 当前下标
search_loop:
mov eax, [data + esi * 4] ; 加载当前元素
cmp eax, [target] ; 是否等于目标值
je found ; 找到了
inc esi ; 下标 +1
loop search_loop
; 没找到
mov eax, 4
mov ebx, 1
mov ecx, notfound_msg
mov edx, notfound_len
int 0x80
jmp exit
found:
; 找到了(esi 是下标)
mov eax, 4
mov ebx, 1
mov ecx, found_msg
mov edx, found_len
int 0x80
; 将下标转为 ASCII 字符输出
add esi, '0' ; 单数字下标转字符
push esi ; 压栈作为临时存储
mov eax, 4
mov ebx, 1
mov ecx, esp ; 栈顶地址
mov edx, 1
int 0x80
pop esi
; 输出换行
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, 1
int 0x80
exit:
mov eax, 1
mov ebx, 0
int 0x80
; 在数组中查找指定值
section .data
data dd 12, 45, 67, 23, 89, 34, 56, 78, 90, 11
data_len equ ($ - data) / 4
target dd 23 ; 要查找的值
found_msg db 'Found at index: ', 0
found_len equ $ - found_msg
notfound_msg db 'Not found (runoob)', 0xA
notfound_len equ $ - notfound_msg
newline db 0xA
section .text
global _start
_start:
mov ecx, data_len ; 循环次数
mov esi, 0 ; 当前下标
search_loop:
mov eax, [data + esi * 4] ; 加载当前元素
cmp eax, [target] ; 是否等于目标值
je found ; 找到了
inc esi ; 下标 +1
loop search_loop
; 没找到
mov eax, 4
mov ebx, 1
mov ecx, notfound_msg
mov edx, notfound_len
int 0x80
jmp exit
found:
; 找到了(esi 是下标)
mov eax, 4
mov ebx, 1
mov ecx, found_msg
mov edx, found_len
int 0x80
; 将下标转为 ASCII 字符输出
add esi, '0' ; 单数字下标转字符
push esi ; 压栈作为临时存储
mov eax, 4
mov ebx, 1
mov ecx, esp ; 栈顶地址
mov edx, 1
int 0x80
pop esi
; 输出换行
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, 1
int 0x80
exit:
mov eax, 1
mov ebx, 0
int 0x80
二维数组
二维数组在内存中按行展开为一维存储。访问 arr[i][j] 的地址公式为:
地址 = 基址 + (i*列数 + j) * 元素大小
实例
; 文件路径:2d_array.asm
; 二维数组的定义和访问
section .data
; 3 行 4 列的二维数组
matrix dd 1, 2, 3, 4
dd 5, 6, 7, 8
dd 9, 10, 11, 12
rows equ 3
cols equ 4
elem_size equ 4 ; 双字 = 4 字节
section .text
global _start
_start:
; 访问 matrix[1][2] = 7(第2行第3列)
; 地址 = matrix + (1*4 + 2) * 4 = matrix + 24
mov eax, [matrix + (1 * cols + 2) * elem_size]
; eax = 7
; 使用寄存器动态计算(假设 i=2, j=1)
; matrix[2][1] = 10(第3行第2列)
mov esi, 2 ; 行 i = 2
mov edi, 1 ; 列 j = 1
mov eax, cols ; 列数
mul esi ; eax = i * cols = 2*4 = 8
add eax, edi ; eax = i*cols + j = 8+1 = 9
; eax = eax * elem_size
; 注意:MUL 的结果在 EAX 中,这里直接使用
mov eax, [matrix + eax * elem_size]
; eax = 10
; 遍历二维数组所有元素
mov ecx, rows * cols ; 总元素数 = 12
mov esi, 0 ; 下标
mov ebx, 0 ; 累加和
traverse:
add ebx, [matrix + esi * elem_size]
inc esi
loop traverse
; ebx = 1+2+3+...+12 = 78
mov eax, 1
int 0x80
; 二维数组的定义和访问
section .data
; 3 行 4 列的二维数组
matrix dd 1, 2, 3, 4
dd 5, 6, 7, 8
dd 9, 10, 11, 12
rows equ 3
cols equ 4
elem_size equ 4 ; 双字 = 4 字节
section .text
global _start
_start:
; 访问 matrix[1][2] = 7(第2行第3列)
; 地址 = matrix + (1*4 + 2) * 4 = matrix + 24
mov eax, [matrix + (1 * cols + 2) * elem_size]
; eax = 7
; 使用寄存器动态计算(假设 i=2, j=1)
; matrix[2][1] = 10(第3行第2列)
mov esi, 2 ; 行 i = 2
mov edi, 1 ; 列 j = 1
mov eax, cols ; 列数
mul esi ; eax = i * cols = 2*4 = 8
add eax, edi ; eax = i*cols + j = 8+1 = 9
; eax = eax * elem_size
; 注意:MUL 的结果在 EAX 中,这里直接使用
mov eax, [matrix + eax * elem_size]
; eax = 10
; 遍历二维数组所有元素
mov ecx, rows * cols ; 总元素数 = 12
mov esi, 0 ; 下标
mov ebx, 0 ; 累加和
traverse:
add ebx, [matrix + esi * elem_size]
inc esi
loop traverse
; ebx = 1+2+3+...+12 = 78
mov eax, 1
int 0x80
冒泡排序完整示例
实例
; 文件路径:bubble_sort.asm
; 冒泡排序算法
section .data
array dd 64, 34, 25, 12, 22, 11, 90, 78
array_len equ ($ - array) / 4
section .text
global _start
_start:
mov ecx, array_len ; 外层循环:n 次
dec ecx ; 外层只需 n-1 次
outer_loop:
push ecx ; 保存外层计数器
mov esi, 0 ; 内层下标从 0 开始
mov ecx, array_len - 1 ; 内层循环次数
inner_loop:
mov eax, [array + esi * 4] ; a[j]
mov ebx, [array + esi * 4 + 4] ; a[j+1]
cmp eax, ebx ; a[j] > a[j+1] ?
jle no_swap ; 否,不交换
; 交换 a[j] 和 a[j+1]
mov [array + esi * 4], ebx
mov [array + esi * 4 + 4], eax
no_swap:
inc esi
loop inner_loop
pop ecx
loop outer_loop
; 数组现在已排序:11, 12, 22, 25, 34, 64, 78, 90
mov eax, 1
mov ebx, 0
int 0x80
; 冒泡排序算法
section .data
array dd 64, 34, 25, 12, 22, 11, 90, 78
array_len equ ($ - array) / 4
section .text
global _start
_start:
mov ecx, array_len ; 外层循环:n 次
dec ecx ; 外层只需 n-1 次
outer_loop:
push ecx ; 保存外层计数器
mov esi, 0 ; 内层下标从 0 开始
mov ecx, array_len - 1 ; 内层循环次数
inner_loop:
mov eax, [array + esi * 4] ; a[j]
mov ebx, [array + esi * 4 + 4] ; a[j+1]
cmp eax, ebx ; a[j] > a[j+1] ?
jle no_swap ; 否,不交换
; 交换 a[j] 和 a[j+1]
mov [array + esi * 4], ebx
mov [array + esi * 4 + 4], eax
no_swap:
inc esi
loop inner_loop
pop ecx
loop outer_loop
; 数组现在已排序:11, 12, 22, 25, 34, 64, 78, 90
mov eax, 1
mov ebx, 0
int 0x80
汇编语言中的数组没有任何边界检查。访问越界的索引不会报错,而是会静默地读写邻近内存中的数据,这可能导致难以调试的 bug。务必自己确保索引在合法范围内。
