1、变量名是内存地址的别名
int val3 = 2;
*(int *)&val2 = 2; // 汇编代码完全一致。先取地址,再对地址对于内存赋值
我觉得是编译器直接优化掉了中间过程,直接往栈上内存写值。
*(int *)&val2 = 2;
而如果取地址,再赋值的话,会有额外的指令操作:
- “lea rax, [rbp-16];将rbp-16这个值直接赋给rax,而不是把rbp-16处的内存地址里的数据赋给rax”。先把val1的地址[rbp - 16],放进寄存器rax中。(mov rax,[rbp - 16]则是把内存地址为rbp - 16处的数据赋给rax)
- 再把 rax中的地址值,赋值给p1,p1对于栈位置[rbp - 8]
- 再把p1的值放到寄存器中rax
- 再对rax中地址的值,写入2
这里为什么需要寄存器rax参与?
针对第五条汇编,lea指令加载有效地址(load effective address),从内存读取数据到寄存器(是不是一定要用寄存器,可不可以直接就把地址给[rbp - 8]???,再直接向[rbp - 8] 赋值为2???)
2、暴露指针的风险
如果代码中暴露了代码的地址(变量、函数),我们都可以直接通过该地址来改写这块内存的内容。如果没有硬件的限制,如MMU检查读写权限,就很难发现这类错误。
同时,如下代码,通过栈(向下生长)中变量的地址,不断打印出朝高地址方向的地址,直接暴露出函数的调用栈。