很多人觉得汇编语言是“古董级”的东西,学它没用。其实不然。当你想了解程序在CPU里到底是怎么跑的,或者需要极致优化性能时,汇编依然有它的位置。比如你在调试一段C代码,发现某个函数特别慢,查看编译后的汇编码,可能一眼就能看出问题出在哪儿。
先看一个最简单的例子:输出“Hello, World!”
在Linux环境下,用x86-64汇编可以这样写:
.section .data
msg: .ascii "Hello, World!\n"
.section .text
.globl _start
_start:
# 系统调用 write(1, msg, 14)
mov $1, %rax # 系统调用号:write
mov $1, %rdi # 文件描述符:stdout
mov $msg, %rsi # 要输出的字符串地址
mov $14, %rdx # 字符串长度
syscall
# 退出程序 exit(0)
mov $60, %rax # 系统调用号:exit
mov $0, %rdi # 退出状态码
syscall
这段代码没有用任何高级库,直接通过系统调用和内核打交道。你可能会问,为什么不用printf?因为那背后也是汇编实现的。现在这短短十几行,就是最底层的逻辑。
再来看一个实用点的例子:计算1到100的和
假设你要写个循环累加,不靠C,自己动手:
.section .text
.globl _start
_start:
mov $0, %eax # 累加器清零
mov $1, %ebx # 当前数值,从1开始
mov $100, %ecx # 循环次数
loop_start:
add %ebx, %eax # 把当前值加到累加器
inc %ebx # 当前值+1
loop loop_start # ecx减1,不为0则跳转
# 程序结束,把结果放在退出码里
mov %eax, %rdi # 结果传给退出码
mov $60, %rax # exit系统调用
syscall
运行完后,你可以通过echo $?查看退出码,就是1到100的和——5050。虽然不能打印在屏幕上,但这是最原始的调试方式,老程序员都这么干过。
为什么还要学汇编?
你可能一辈子都不会写一个完整的汇编程序,但懂一点能帮你理解很多事。比如变量是怎么存的,函数调用时栈是怎么压入弹出的。你在写C或C++时,一旦遇到段错误,如果能看懂崩溃时的汇编码,定位问题会快得多。
另外,像逆向工程、病毒分析、嵌入式开发这些领域,汇编是基本功。哪怕只是看看反汇编工具(如GDB里的disassemble)输出的内容,也能让你对程序有更深的理解。