--- ingested: true ingestedAt: 2026-05-17 --- 标题: 汇编语言 - 数字处理 链接: https://www.runoob.com/assembly/assembly-numbers.html 内容: ## 汇编语言 - 数字处理 在汇编语言中处理数字涉及到进制转换、ASCII 转换和算术运算的结合。本章详细讲解如何在程序中输入和输出各种进制的数字。 --- ## 数字的表示方式 计算机内部所有数字都以二进制存储,但程序中可以用不同进制表示: | 进制 | 基数 | NASM 写法 | 数字范围(32位) | | ---- | -- | ---------------------- | --------- | | 二进制 | 2 | 0b10101010 或 10101010b | 0 和 1 | | 八进制 | 8 | 0o52 或 52o | 0-7 | | 十进制 | 10 | 42 | 0-9 | | 十六进制 | 16 | 0x2A 或 2Ah | 0-9, A-F | --- ## ASCII 与数字的关系 在屏幕上输入和输出的都是 ASCII 字符,而非二进制数值。 理解字符与数值的转换是汇编数字处理的核心: | 字符 | ASCII 值 | 对应的数值 | | --- | -------- | -------- | | '0' | 0x30(48) | 0 | | '1' | 0x31(49) | 1 | | '9' | 0x39(57) | 9 | | 'A' | 0x41(65) | 10(十六进制) | | 'F' | 0x46(70) | 15(十六进制) | | 'a' | 0x61(97) | 10(十六进制) | 字符转数字:`数值 = ASCII码 - '0'`(即 `ASCII码 - 0x30`) 数字转字符:`ASCII码 = 数值 + '0'`(即 `数值 + 0x30`) --- ## 输入数字(字符串转数值) 用户输入的是字符序列,需要转换为二进制数值才能用于计算: ## 实例 ; 文件路径:string\_to\_int.asm ; 将十进制数字字符串转换为整数值 section .data input\_str db '12345', 0 ; 输入字符串 "12345" input\_len dd 5 ; 字符串长度 section .bss result\_val resd 1 ; 存放转换后的数值 section .text global \_start \_start: ; 转换算法:result = 0 ; result = result \* 10 + (当前字符 - '0') mov esi, input\_str ; esi 指向输入字符串 mov ecx, \[input\_len\] ; ecx = 循环次数 mov eax, 0 ; eax = 累加结果 convert\_loop: mov ebx, 10 mul ebx ; eax = eax \* 10 ; mul ebx: edx:eax = eax \* ebx mov bl, \[esi\] ; 读取当前字符 sub bl, '0' ; 转为数值:字符 - '0' movzx ebx, bl ; 零扩展 ebx(bl -> ebx) add eax, ebx ; eax = eax + 当前位数值 inc esi ; 移动到下一个字符 loop convert\_loop ; eax 现在 = 12345 mov \[result\_val\], eax ; 保存结果 mov eax, 1 mov ebx, 0 int 0x80 > 算法解析:以 "12345" 为例: > 第1轮:result = 0×10 + 1 = 1 > 第2轮:result = 1×10 + 2 = 12 > 第3轮:result = 12×10 + 3 = 123 > ...最终得到 12345。 --- ## 输出数字(数值转字符串) 将二进制数值转为可显示的十进制字符串: ## 实例 ; 文件路径:int\_to\_string.asm ; 将整数值转换为十进制字符串输出 section .data number dd 12345 ; 要输出的数字 newline db 0xA section .bss output\_buf resb 12 ; 输出缓冲区(最大 32 位整数是 10 位 + 符号 + null) section .text global \_start \_start: mov eax, \[number\] ; 加载数字 mov edi, output\_buf + 11 ; edi 指向缓冲区末尾 mov byte \[edi\], 0 ; null 终止符(方便调试) dec edi mov ebx, 10 ; 除数 = 10 mov ecx, 0 ; 位数计数器 convert\_digit: mov edx, 0 ; 清零 edx(div 需要 edx:eax) div ebx ; eax = eax/10, edx = eax%10 add dl, '0' ; 余数转字符 mov \[edi\], dl ; 存入缓冲区(从后往前) dec edi ; 缓冲区指针前移 inc ecx ; 位数 +1 cmp eax, 0 ; 商是否为 0? jne convert\_digit ; 否,继续循环 ; 现在 edi+1 指向第一个有效数字字符 inc edi ; 修正指针,指向字符串开头 ; 计算有效字符长度 ; output\_buf + 11 - edi 即有效长度 ; 输出数字 mov eax, 4 mov ebx, 1 mov ecx, edi ; 指向转换后的字符串 ; 计算长度 mov edx, output\_buf + 11 sub edx, edi ; edx = 缓冲区末尾 - 字符串开头 int 0x80 ; 输出换行 mov eax, 4 mov ebx, 1 mov ecx, newline mov edx, 1 int 0x80 mov eax, 1 mov ebx, 0 int 0x80 运行结果: $ nasm -f elf32 int_to_string.asm -o int_to_string.o $ ld -m elf_i386 int_to_string.o -o int_to_string $ ./int_to_string 12345 --- ## 十六进制输入输出 处理十六进制需要同时处理 0-9 和 A-F/a-f 的转换: ## 实例 ; 文件路径:hex\_output.asm ; 将数值以十六进制格式输出 section .data number dd 0x1A2B3C4D ; 要输出的值 hex\_prefix db '0x' hex\_prefix\_len equ $ \- hex\_prefix newline db 0xA section .bss hex\_buf resb 10 ; 8 个十六进制位 + 前缀 + null section .text global \_start \_start: ; 输出前缀 "0x" mov eax, 4 mov ebx, 1 mov ecx, hex\_prefix mov edx, hex\_prefix\_len int 0x80 mov eax, \[number\] ; 要转换的数值 mov edi, hex\_buf + 8 ; 缓冲区末尾(8 个十六进制位) mov ecx, 8 ; 循环 8 次(32 位 = 8 个十六进制位) hex\_loop: mov edx, 0 ; 准备除法 mov ebx, 16 ; 除以 16 div ebx ; 现在:eax 除以 ebx ; 注意:这里 div ebx 实际是 edx:eax / ebx ; 余数在 edx(0-15),商在 eax mov ebx, eax ; 保存商 ; 将余数转为十六进制字符 mov al, dl cmp al, 10 jl digit\_09 ; 0-9 add al, 'A' \- 10 ; A-F jmp store\_char digit\_09: add al, '0' ; 0-9 store\_char: dec edi mov \[edi\], al mov eax, ebx ; 恢复商 loop hex\_loop ; 移除了loop,直接使用递减方式 ; 输出十六进制数字(8 位) mov eax, 4 mov ebx, 1 mov ecx, hex\_buf mov edx, 8 int 0x80 ; 输出换行 mov eax, 4 mov ebx, 1 mov ecx, newline mov edx, 1 int 0x80 mov eax, 1 mov ebx, 0 int 0x80 --- ## 完整示例:简单的加法计算器 从键盘输入两个一位数,计算它们的和并输出: ## 实例 ; 文件路径:simple\_calc.asm ; 输入两个一位数(0-9),计算和并输出 section .data prompt1 db 'Enter first number (0-9): ' prompt1\_len equ $ \- prompt1 prompt2 db 'Enter second number (0-9): ' prompt2\_len equ $ \- prompt2 result\_msg db 'Sum = ' result\_msg\_len equ $ \- result\_msg newline db 0xA section .bss num1 resb 2 ; 第一个数的输入(1位+换行) num2 resb 2 ; 第二个数的输入 result\_str resb 3 ; 结果字符串(最多2位+换行) section .text global \_start \_start: ; 提示输入第一个数 mov eax, 4 mov ebx, 1 mov ecx, prompt1 mov edx, prompt1\_len int 0x80 ; 读取第一个数 mov eax, 3 mov ebx, 0 mov ecx, num1 mov edx, 2 int 0x80 ; 提示输入第二个数 mov eax, 4 mov ebx, 1 mov ecx, prompt2 mov edx, prompt2\_len int 0x80 ; 读取第二个数 mov eax, 3 mov ebx, 0 mov ecx, num2 mov edx, 2 int 0x80 ; 计算和 mov al, \[num1\] ; 读取第一个数(ASCII 字符) sub al, '0' ; 转为数值 mov bl, \[num2\] ; 读取第二个数 sub bl, '0' ; 转为数值 add al, bl ; 求和 ; 将结果转为字符串 mov ah, 0 ; 准备除法 mov bl, 10 ; 除以 10 div bl ; al = 十位数, ah = 个位数 ; 保存结果 add al, '0' ; 十位转字符 mov \[result\_str\], al ; 存十位 add ah, '0' ; 个位转字符 mov \[result\_str + 1\], ah ; 存个位 mov byte \[result\_str + 2\], 0xA ; 换行 ; 输出结果信息 mov eax, 4 mov ebx, 1 mov ecx, result\_msg mov edx, result\_msg\_len int 0x80 ; 输出计算结果(可能 1 或 2 位) mov eax, 4 mov ebx, 1 mov ecx, result\_str cmp byte \[result\_str\], '0' je output\_one\_digit ; 如果十位是 '0',只输出个位 mov edx, 3 ; 2 位 + 换行 jmp do\_output output\_one\_digit: inc ecx ; 跳过十位的 '0' mov edx, 2 ; 1 位 + 换行 do\_output: int 0x80 mov eax, 1 mov ebx, 0 int 0x80 运行结果: $ nasm -f elf32 simple_calc.asm -o simple_calc.o $ ld -m elf_i386 simple_calc.o -o simple_calc $ ./simple_calc Enter first number (0-9): 7 Enter second number (0-9): 8 Sum = 15 > 在汇编中处理数字输入输出需要对 ASCII 编码和进制转换有清晰的理解。数字转字符串(int to string)和字符串转数字(string to int)是最基本的两个工具函数,建议写好后保存为模板,后续可直接复用。