-
介绍篇
-
在线执行
-
悬停,显示帮助
-
右键,查看文档
- template
example_1
int main(){
int a = 123;
return 0;
}
(gdb) disas
Dump of assembler code for function main():
0x0000555555555129 <+0>: endbr64
0x000055555555512d <+4>: push %rbp
0x000055555555512e <+5>: mov %rsp,%rbp
=> 0x0000555555555131 <+8>: movl $0x7b,-0x4(%rbp)
0x0000555555555138 <+15>: mov $0x0,%eax
0x000055555555513d <+20>: pop %rbp
0x000055555555513e <+21>: ret
End of assembler dump.
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 123
mov eax, 0
pop rbp
ret
example_2
int main(){
static int a = 123;
return 0;
}
(gdb) disas
Dump of assembler code for function main():
0x0000555555555129 <+0>: endbr64
0x000055555555512d <+4>: push %rbp
0x000055555555512e <+5>: mov %rsp,%rbp
=> 0x0000555555555131 <+8>: mov $0x0,%eax
0x0000555555555136 <+13>: pop %rbp
0x0000555555555137 <+14>: ret
End of assembler dump.
main:
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
example_3
int main(){
const int a = 123;
return 0;
}
(gdb) disas
Dump of assembler code for function main():
0x0000555555555129 <+0>: endbr64
0x000055555555512d <+4>: push %rbp
0x000055555555512e <+5>: mov %rsp,%rbp
=> 0x0000555555555131 <+8>: movl $0x7b,-0x4(%rbp)
0x0000555555555138 <+15>: mov $0x0,%eax
0x000055555555513d <+20>: pop %rbp
0x000055555555513e <+21>: ret
End of assembler dump.
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 123
mov eax, 0
pop rbp
ret
inline函数
使用 inline
关键字可以对函数进行内联展开(inline expansion),这意味着函数的代码会被直接插入调用它的地方,而不是通过函数调用的方式执行。inline 在编译器的编译阶段被处理)(需要注意的是,inline 仅仅是对编译器的一个提示,编译器完全有权决定是否内联展开函数。它可能会忽略 inline 提示,特别是在一些情况下,如递归函数、虚函数、函数体过大等情况)。使用 inline
可以有以下优点和缺点:
优点:
- 提高执行速度:内联展开可以消除函数调用的开销,因为不需要在运行时跳转到函数的位置执行代码。这可以在一定程度上提高程序的执行速度,尤其是对于短小的函数。
- 节省函数调用栈:函数调用会在运行时创建调用栈,包含函数参数和返回地址等信息。内联展开可以避免创建和销毁调用栈,从而减少了一部分内存开销,并且可以提高程序的效率。
- 编译器优化:内联函数通常会被编译器视为宏替换,使编译器有更多的优化机会,如常量替换、循环展开等。这可以进一步提高程序的性能。
缺点:
- 增加代码尺寸:内联展开会导致函数的代码被复制到多个调用点,增加了可执行文件的尺寸。对于一些较大的函数,这可能会增大可执行文件的大小,从而降低缓存效率。
- 增加编译时间:内联函数的代码会在每个调用点都进行复制和插入,这可能导致编译时间的增加,特别是当内联函数被频繁调用时。
- 可读性和维护性下降:内联函数的代码被复制到多个地方,可能会导致代码重复,并且使代码变得分散。这可能会降低代码的可读性和维护性,特别是在需要对函数进行修改或调试时。
综上所述,使用 inline
可以提高程序的执行效率和编译器的优化机会。然而,应该谨慎使用 inline
,并根据函数的大小、调用频率以及代码的可读性和维护性需求来权衡其优缺点。对于短小的、频繁调用且性能关键的函数,使用 inline
可能是合适的选择。
cg
- EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP 寄存器详解
rax rsp是什么
rax(全名为 Accumulator register)是通用目的寄存器之一。在64位模式下,rax 是一个64位寄存器,可以用于存储和操作64位的数据。rax 在函数调用中常用于存储函数返回值。
rsp(全名为 Stack Pointer register)是栈指针寄存器,它指向当前栈的顶部。栈是一块特殊的内存区域,用于存储函数调用过程中的局部变量、函数参数和返回地址等。rsp 的值会随着函数的调用和返回而不断变化,用于管理栈帧的创建和销毁。
-
sub rsp, 0x10: 这条指令将栈指针 rsp 减去一个常数值 0x10,即在栈上分配 16 字节的空间。这通常用于为函数调用准备局部变量或保存临时数据。
-
lea rax, [rsp+0xf]: 这条指令使用地址计算,即将 rsp 寄存器的值加上常数 0xf,结果存储在 rax 寄存器中。这样做的目的是获得 rsp 寄存器向上对齐到 16 字节边界的地址。(lea destination, source, destination 是目标寄存器,用于存储计算得到的地址,source 则表示源操作数,可以是一个内存地址、寄存器或立即数)
-
and rax, 0xfffffffffffffff0: 这条指令将 rax 寄存器的值与常数 0xfffffffffffffff0 进行按位与操作,将 rax 的低 4 位清零,得到一个 16 字节对齐的地址。
-
mov QWORD PTR [rdi], rax: 这条指令将 rax 的值存储到以 rdi 寄存器作为地址的内存位置,即将 rax 的值存储到 rdi 指向的内存中。
从内存中读取 rsp+0x8
地址处的一个 64 位值,然后检查这个值是否为零。根据比较结果,当这个值不为零时,跳转到地址 401145
继续执行后续的指令。
-
mov rax, QWORD PTR [rsp+0x8]
: 这条指令将rsp
寄存器的值加上偏移量0x8
,然后将结果作为地址,从内存中读取一个 8 字节(64位)的值,并将其存储到rax
寄存器中。这样做的目的是获取rsp+0x8
地址处存储的数据。 -
cmp BYTE PTR [rax], 0x0
: 这条指令将rax
寄存器的值作为地址,从内存中读取一个字节(8位)的值,与常数0x0
进行比较。这里的目的是检查rax
寄存器指向的内存位置中的值是否为零(空字节)。 -
jne 401145 <main+0x85>
: 这条指令根据上一条cmp
指令的结果,当比较结果为不相等(即不为零)时,跳转到地址401145
处执行。这里是根据条件执行的跳转,当前偏移量0x85
是相对于当前指令的偏移地址。