欢迎光临
我们一直在努力

(学习笔记)3.4 访问信息(3.4.3 数据传送示例&3.4.4 压入和弹出栈数据)

文章目录

  • 线索栏
  • 笔记栏
    • 1. exchange函数分析 (图3-7)
      • 1)C代码
      • 2)汇编代码与解读
      • 3)揭示的两个关键事实
    • 2. 栈的概念与指令
      • 1)栈
      • 2)栈指针 %rsp
      • 3)pushq S指令(将四字S压入栈)
      • 4)popq D指令(从栈弹出一个四字到D)
    • 3. 等效操作与优化
    • 3. 栈的访问
    • 4.练习题
      • 练习题3.4
      • 练习题3.5
  • 总结栏

线索栏

  • 示例解析:exchange函数的汇编代码如何具体体现了指针的间接引用操作(*xp)?
  • 关键特性:从 exchange的汇编代码中可以观察到关于C语言的哪两个重要事实?
  • 类型转换练习:练习题3.4中,如何根据 src_t和 dest_t的类型(大小、符号性)选择正确的数据传送和扩展指令?
  • 反编译练习:练习题3.5中,如何根据给定的汇编代码反向推导出等效的C代码逻辑?
  • 栈的操作:pushq S和 popq D指令在底层具体执行了哪两个步骤?(结合栈指针 %rsp和内存)
  • 栈的生长方向:x86-64的栈是向高地址还是低地址生长?栈指针 %rsp保存的是什么地址?
  • 指令等价:为什么说 pushq %rbp在功能上等价于 subq $8, %rsp和 movq %rbp,(%rsp)两条指令,但又更优?

  • 笔记栏

    1. exchange函数分析 (图3-7)

    在这里插入图片描述

    1)C代码

    long exchange(long *xp, long y) { long x = *xp; *xp = y; return x; }

    2)汇编代码与解读

    // xp in %rdi, y in %rsi
    movq (%rdi), %rax // 1. 从内存(%rdi指向的地址)读取值到%rax。对应: long x = *xp;
    movq %rsi, (%rdi) // 2. 将%rsi的值写入内存(%rdi指向的地址)。对应: *xp = y;
    ret // 3. 返回,返回值在%rax (即x)

    3)揭示的两个关键事实

    (1)C指针即地址:指针变量(xp)本身存储在寄存器中(%rdi)。对指针解引用(*xp)在汇编中体现为以该寄存器值为地址的内存访问((%rdi))。 (2)局部变量常驻寄存器:局部变量 x并未存入栈内存,而是直接保留在寄存器 %rax中。这优化了访问速度。

    2. 栈的概念与指令

    1)栈

    一种后进先出(LIFO)的数据结构,在内存中实现,用于支持过程调用。栈顶是最近插入的元素,地址最低。

    2)栈指针 %rsp

    始终保存栈顶元素的内存地址。

    3)pushq S指令(将四字S压入栈)

    (1)%rsp ← %rsp – 8(栈指针减8,栈向低地址方向增长) (2)M[%rsp] ← S(将源操作数S的值写入新的栈顶地址)

    4)popq D指令(从栈弹出一个四字到D)

    (1)D ← M[%rsp](从当前栈顶地址读取值到目的操作数D) (2)%rsp ← %rsp + 8(栈指针加8)

    3. 等效操作与优化

    (1)pushq %rbp功能上等价于:

    subq $8, %rsp // 1. 递减栈指针
    movq %rbp, (%rsp) // 2. 存储值

    (2)popq %rax功能上等价于

    movq (%rsp), %rax // 1. 读取值
    addq $8, %rsp // 2. 递增栈指针

    (3)为什么用单独指令 pushq和 popq的机器码更短(1字节 vs 多条指令合计8字节),且是通用指令,硬件可能优化其执行。

    3. 栈的访问

    程序可以用标准内存寻址访问栈内任何位置。例如,movq 8(%rsp), %rdx将栈顶下方(地址更高处)的第二个四字复制到 %rdx。

    4.练习题

    练习题3.4

    在这里插入图片描述 在这里插入图片描述

    练习题3.5

    在这里插入图片描述

    decode1:
    movq (%rdi), %r8 // t1 = *xp
    movq (%rsi), %rcx // t2 = *yp
    movq (%rdx), %rax // t3 = *zp
    movq %r8, (%rsi) // *yp = t1
    movq %rcx, (%rdx) // *zp = t2
    movq %rax, (%rdi) // *xp = t3
    ret

    void decode1(long *xp, long *yp, long *zp) {
    long t1 = *xp;
    long t2 = *yp;
    long t3 = *zp;
    *yp = t1;
    *zp = t2;
    *xp = t3;
    }

    在这里插入图片描述


    总结栏

    本节通过具体示例和练习,深化了对数据传送指令的理解,并引入了程序栈这一关键数据结构。

  • 指针的本质:在机器级,C语言的“指针”就是地址,间接引用操作被翻译为以寄存器值为地址的加载(mov源为内存)或存储(mov目的为内存)指令。exchange函数是此概念的完美例证。
  • 类型转换的映射:C语言中的强制类型转换(尤其是涉及大小和符号变化时)直接对应到特定的数据传送指令(mov, movs,movz)。练习题3.4要求根据源/目类型精确选择指令,这是理解编译器行为的基础。
  • 逆向工程能力:练习题3.5训练了从汇编“反编译”回C代码的能力,关键在于追踪数据流(哪个值从何处读,写向何处),并识别出临时变量和指针操作。
  • 栈:程序运行的基石: (1)操作:pushq和 popq指令以栈指针 %rsp为中心,通过调整 %rsp和内存访问来工作。 (2)方向:栈向低地址增长(push时 %rsp减小)。 (3)高效性:单独的栈指令比等效的算术+内存指令序列更紧凑、可能更高效。 (4)通用性:栈内存可以像普通内存一样被寻址访问。
  • 栈的引入为下一节“算术和逻辑操作”以及更重要的“控制”和“过程”章节铺平了道路。理解栈是理解函数调用、局部变量存储和程序控制流管理的关键。

    赞(0)
    未经允许不得转载:171主机测评 » (学习笔记)3.4 访问信息(3.4.3 数据传送示例&3.4.4 压入和弹出栈数据)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址