【Solidity Yul Assembly】2.2 | How Solidity Uses Memory

  • 0xE
  • 发布于 2024-08-22 15:09
  • 阅读 1052

Solidity 是如何使用内存的?

Solidity 是如何使用内存的

  • Solidity 将地址 [0x00 - 0x20) 和 [0x20 - 0x40) 分配为哈希操作的“临时空间”。

  • Solidity 预留地址 [0x40 - 0x60) 作为“空闲内存指针”,通常指向当前未被使用的内存区域。

  • 地址 [0x60 - 0x80) 保持为空,作为 32 字节的零值插槽,用于需要零值的场合。

  • 内存使用从 [0x80 - ...) 开始。

  • Solidity 使用内存的场景:

    • abi.encodeabi.encodePacked.
    • 结构体和数组(但是你需要明确使用 memory 关键字)。
    • 在函数参数中,使用了 memory 关键字的结构体或数组。
    • 内存中的对象是按顺序排列的,因此数组不能像存储(storage)那样进行动态添加元素(push)。
  • 在 Yul 中,

    • 变量存储在内存中的位置即为数组的起始位置。
    • 对于动态数组,需额外添加 32 字节(即 0x20)以跳过数组长度信息,从而访问数组的实际元素。这是因为在 Solidity 中,动态数组会存储数组长度(32 字节),随后才是实际的数组元素。

结构体

contract Memory {
    struct Point {
        uint256 x;
        uint256 y;
    }

    event MemoryPointer(bytes32);
    event MemoryPointerMsize(bytes32, bytes32);

    function memPointerV1() external {
        bytes32 x40;
        assembly {
            x40 := mload(0x40)
        }
        emit MemoryPointer(x40);  // 0x0000000000000000000000000000000000000000000000000000000000000080

        Point memory p = Point({x: 1, y: 2});
        assembly {
            x40 := mload(0x40)
        }
        emit MemoryPointer(x40);  // 0x00000000000000000000000000000000000000000000000000000000000000c0
    }
}

通过以上代码可以看出,“空闲内存指针”本来指向 0x80,分配了结构体后,指向了 0xc0。这个差值为 64 字节,即 2 个 32 字节,正好是结构体中的 xy

function memPointerV2() external {
    bytes32 x40;
    bytes32 _msize;
    assembly {
        x40 := mload(0x40)
        _msize := msize()
    }
    emit MemoryPointerMsize(x40, _msize);
    // 0x0000000000000000000000000000000000000000000000000000000000000080
    // 0x0000000000000000000000000000000000000000000000000000000000000060

    Point memory p = Point({x: 1, y: 2});
    assembly {
        x40 := mload(0x40)
        _msize := msize()
    }
    emit MemoryPointerMsize(x40, _msize);
    // 0x00000000000000000000000000000000000000000000000000000000000000c0
    // 0x00000000000000000000000000000000000000000000000000000000000000c0

    assembly {
        pop(mload(0xff))
        x40 := mload(0x40)
        _msize := msize()
    }
    emit MemoryPointerMsize(x40, _msize);
    // 0x00000000000000000000000000000000000000000000000000000000000000c0
    // 0x0000000000000000000000000000000000000000000000000000000000000120
}

msize() 返回最大可访问的内存地址。当内存中未存放数据时,该值为 0x60;而当存放了一个结构体后,该值与“空闲内存指针”的值一致,为 0xc0。
在最后一部分代码中使用 pop(mload(0xff)) 的主要目的是为了读取内存槽 0xff 的数据,即[0xff - 0x120),让可访问空间变大,因此此时 msize() 变为 0x120。

定长数组


function fixedArray() external {
    bytes32 x40;
    assembly {
        x40 := mload(0x40)
    }
    e...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。有工作机会可加v:__0xE__