Files
SiMengWebSite_Notes/docs/notes/programming/solidity/other/miscellaneous.md
祀梦 086835d409 docs(solidity): 更新memory和storage存储类型的文档
补充关于Solidity中memory和storage存储类型的详细说明,包括生命周期、成本、默认行为、拷贝语义、内部函数引用传递、动态数组限制等内容。添加代码示例和最佳实践建议,帮助开发者更好地理解和使用不同存储类型。
2025-10-20 20:25:23 +08:00

3.4 KiB

title, createTime, permalink
title createTime permalink
一些没分类的小知识 2025/10/12 15:34:38 /programming/solidity/other/miscellaneous/

关于 memory 和 storage 存储类型

  • storage:合约的持久化状态数据,保存在链上状态。对 storage 的写入最昂贵,读取也比内存贵;修改会永久生效。
  • memory:函数调用期间的临时数据,函数返回后即释放。对 memory 的更改不会持久化。
  • (补充)calldata:外部函数参数的只读数据位置,零拷贝、不可修改,用于节省 gas。

生命周期与成本

  • storage 写入昂贵、读取较贵;适合保存需要长期存在的状态。
  • memory 在函数结束时释放,读取/写入相对便宜;适合临时计算与返回值。
  • 复杂引用类型(数组、structmappingstringbytes)在函数参数或局部变量处通常必须显式标注数据位置。

默认与必须声明

  • 状态变量总是位于 storage(例如 User[] public users;)。
  • 外部函数(external)的复杂类型参数默认是 calldata;内部/公共函数需要显式标注 memorystorage
  • 局部变量的复杂类型必须指定数据位置,否则编译报错。

拷贝与引用语义

  • storage 读取到 memory 会“复制”数据;修改 memory 副本不影响原始 storage
  • 使用 storage 局部变量可以得到对状态数据的“引用”,对其赋值会持久化。
pragma solidity ^0.8.20;

contract Users {
    struct User { string name; uint age; }
    User[] public users;

    function add(string memory name, uint age) external {
        users.push(User(name, age)); // 写入 storage
    }

    function updateName(uint i, string memory newName) external {
        User storage u = users[i];   // storage 引用(指向链上状态)
        u.name = newName;            // 修改持久化生效
    }

    function tryUpdate(uint i) external {
        User memory u = users[i];    // 从 storage 复制到 memory
        u.age = 99;                  // 仅修改副本,不会影响链上状态
    }
}

在内部函数传递 storage 引用

  • 仅内部/私有函数可以接收 storage 引用参数,从而直接修改状态;外部函数参数不能是 storage
pragma solidity ^0.8.20;

contract Users2 {
    struct User { string name; uint age; }
    User[] public users;

    function _inc(User storage u) internal { u.age += 1; }

    function birthday(uint i) external {
        _inc(users[i]); // 传递 storage 引用,持久化修改
    }
}

动态 memory 数组与限制

  • 可在 memory 中构造动态数组:new uint[](n);适合作为返回值或临时计算。
  • mapping 只能存在于 storage,不能在 memory 中创建或拷贝。
pragma solidity ^0.8.20;

contract Arrays {
    function make(uint n) external pure returns (uint[] memory a) {
        a = new uint[](n);
        for (uint i = 0; i < n; i++) a[i] = i;
    }
}

常见坑与实践建议

  • storage 变量整体赋值会进行深拷贝或引用变更(依据类型),要明确拷贝成本与语义。
  • 修改 memory 副本不会持久化;要修改链上状态请使用 storage 引用。
  • 大型 string/bytes/数组memory↔storage 间复制成本高,尽量减少不必要的复制。
  • 外部函数能用 calldata 的地方尽量使用(只读参数),节省 gas。