
VS Code 编写一个最简单 C 语言程序代码: a.c
int main(){
int c = 0;
c++;
return c;
}使用 VC2022 编译器命令行编译,运行,显示计算结果
cl .\a.c
.\a.exe
$LASTEXITCODEcl是VC编译器的编译程序,PowerShell里使用$LASTEXITCODE用来显示之前运行程序的返回值
VC2022 编译使用 dumpbin.exe 工具可以用来显示程序的汇编代码
dumpbin.exe /DISASM .\a.obj
Dump of file .\a.obj
main:
0000000000000000: 48 83 EC 18 sub rsp,18h
0000000000000004: C7 04 24 00 00 00 mov dword ptr [rsp],0
00
000000000000000B: 8B 04 24 mov eax,dword ptr [rsp]
000000000000000E: FF C0 inc eax
0000000000000010: 89 04 24 mov dword ptr [rsp],eax
0000000000000013: 8B 04 24 mov eax,dword ptr [rsp]
0000000000000016: 48 83 C4 18 add rsp,18h
000000000000001A: C3 ret- 下面是对你提供的汇编指令的逐行解释:
汇编代码解析
main:
0000000000000000: 48 83 EC 18 sub rsp,18hsub rsp, 18h: 从栈指针 (rsp) 中减去0x18(24十进制)。这为函数的局部变量和返回地址留出空间。
0000000000000004: C7 04 24 00 00 00 mov dword ptr [rsp],0
00mov dword ptr [rsp], 0: 将0移动到栈顶(即刚刚调整的rsp指向的位置),初始化局部变量为0。
000000000000000B: 8B 04 24 mov eax, dword ptr [rsp]mov eax, dword ptr [rsp]: 将栈顶的值(即之前存储的0)加载到寄存器eax中。
000000000000000E: FF C0 inc eaxinc eax: 将eax中的值加1,所以此时eax的值变为1。
0000000000000010: 89 04 24 mov dword ptr [rsp], eaxmov dword ptr [rsp], eax: 将eax中的值(即1)存回栈顶。
0000000000000013: 8B 04 24 mov eax, dword ptr [rsp]mov eax, dword ptr [rsp]: 再次将栈顶的值(此时为1)加载到eax中。
0000000000000016: 48 83 C4 18 add rsp, 18hadd rsp, 18h: 释放之前为局部变量分配的栈空间,恢复rsp的值。
000000000000001A: C3 retret: 返回到调用该函数的地方。此时,eax中的值(1)通常作为函数的返回值。
总结
这段汇编代码实现了一个简单的函数,它:
- 在栈上分配空间。
- 初始化一个局部变量为
0。 - 将该变量的值加
1。 - 最后返回这个值(
1)。
这个过程展现了如何操作栈以及基本的寄存器使用。
使用 Risc-V linux 来学习 C语言代码,转成 RV 汇编代码

把C语言代码保存成
001.cint main(){ int c = 0; c++; return c; }我们可以使用
gcc命令把源码转成汇编代码来学习gcc -S 001.c gcc -c -march=rv64g 001.c objdump -d 001.o
这三行命令的逐行解释:
1. gcc -S 001.c
- 命令:
gcc -S 001.c 解释:
gcc是 GNU C 编译器。-S选项告诉编译器生成汇编代码,而不是生成目标文件或可执行文件。001.c是输入的 C 源文件。
- 结果: 该命令会生成一个名为
001.s的汇编源文件,包含了从 C 代码转换而来的汇编代码。
2. gcc -c -march=rv64g 001.c
- 命令:
gcc -c -march=rv64g 001.c 解释:
-c选项表示编译源文件为目标文件(即生成.o文件),而不进行链接。-march=rv64g指定生成代码的架构为 RISC-V 64 位通用架构(RV64G)。001.c是输入的 C 源文件。
- 结果: 该命令会生成一个名为
001.o的目标文件,其中包含编译后的机器代码。
3. objdump -d 001.o
- 命令:
objdump -d 001.o 解释:
objdump是一个用来显示目标文件信息的工具。-d选项表示反汇编目标文件,显示机器代码对应的汇编指令。001.o是输入的目标文件。
- 结果: 该命令会输出
001.o文件中机器代码的汇编表示,帮助开发者理解编译后的代码。
总结
这三行命令的流程是:
- 将 C 源代码转换为汇编代码(
gcc -S)。 - 将 C 源代码编译为特定架构的目标文件(
gcc -c)。 - 反汇编目标文件以查看机器代码的汇编指令(
objdump -d)。
如图我们看到C语言转成后的 RISC-V 汇编代码
汇编代码解析
main:
.LFB0:main:: 定义一个名为main的函数。.LFB0:: 这是一个局部符号,通常用于调试信息。
addi sp, sp, -32addi sp, sp, -32: 将栈指针 (sp) 向下移动32字节,为局部变量和返回地址分配空间。
sd s0, 24(sp)sd s0, 24(sp): 将寄存器s0的值存储到栈中,位置为sp + 24。这是保存调用者的s0的值,以便在返回时恢复。
addi s0, sp, 32addi s0, sp, 32: 将s0的值设置为sp + 32。这意味着s0现在指向一个局部变量的地址。
sw zero, -20(s0)sw zero, -20(s0): 将0存储到s0 - 20的位置,即初始化一个局部变量为0。
lw a5, -20(s0)lw a5, -20(s0): 从s0 - 20的位置加载值到a5。此时a5中的值为0。
addiw a5, a5, 1addiw a5, a5, 1: 将a5的值加1,并存储回a5。现在a5的值为1。
sw a5, -20(s0)sw a5, -20(s0): 将a5中的值(1)存储回s0 - 20的位置。
lw a5, -20(s0)lw a5, -20(s0): 再次从s0 - 20的位置加载值到a5,此时a5的值为1。
mv a0, a5mv a0, a5: 将a5的值移动到a0。在 RISC-V 中,a0通常用于函数的返回值。因此,a0现在也为1。
ld s0, 24(sp)ld s0, 24(sp): 从栈中恢复之前保存的s0的值。
addi sp, sp, 32addi sp, sp, 32: 恢复栈指针 (sp),将其向上移动32字节,释放之前分配的栈空间。
jr rajr ra: 跳转到返回地址,结束函数的执行并返回到调用该函数的位置。
总结
这段 RISC-V 汇编代码实现了一个简单的函数,它:
- 在栈上分配空间。
- 保存寄存器
s0的值。 - 初始化一个局部变量为
0。 - 将该变量的值加
1。 - 将结果(
1)作为返回值返回。
这个过程展示了 RISC-V 中栈的使用、寄存器的操作以及函数返回值的传递。
