solidity基础-数据位置

基础介绍

  • storage:状态变量的保存位置,存储在链上,存储成本比较高(比较费gas)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    uint8 public age = 18;
    uint256 a = 1;
    uint256[] arr = [1, 2, 3];

}
  • memory:数据保存在内存中,仅在生命周期内有效

    • 使用的时候分配,超过作用域不可访问,等待回收
    • memory 只能修饰引用数据类型,不能修饰值类型
    • 函数参数(包括返回值)都存储在内存中
    • 引用类型的局部变量,需要显式地指定数据位置

      • 不能在内存中创建动态数据
      • mapping 和 struct 不能在函数中动态创建,必须从状态变量中分配
    • 函数的输入和输出参数,如果是引用类型,则需要使用 memory 标识(callback)
    • 引用类型的局部变量:指定 storage / memory 的区别

      • storage 修改引用数据,会修改状态变量
      • memory 修改引用数据,函数运行完就消失了,修改的值也不会储存在状态中
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    function add(uint256 num1, uint256 num2) public pure returns (uint256 sum) {
        uint256 tmp = num1 + num2; // tmp 存储在 memory 中
        return tmp;
    }

}
  • calldata:保存函数参数的特殊数据位置,是一个只读的位置

    • calldata 只能用在函数的输入和输出参数中
    • calldata 用在输入参数中,比 memory 更省 gas
    • calldata 标记的参数不可被修改, memory 标记的参数是可以修改的
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    function test(uint256[] calldata arr) external pure returns (uint256[] calldata) {
        // arr 不可修改
        return arr;
    }

}

不同数据位置之间的赋值规则

存储变量 => 存储变量

  • 值类型:创建一个新的副本
  • 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    uint256 public state1 = 10;
    uint256 public state2 = 20;

    string public str1 = "ABCD";
    string public str2 = "1234";

    function testA() public returns (uint256) {
        state1 = state2;
        state2 = 30;
        return state1; // 第一次触发返回20  第二次触发返回30
    }

    function testB() public returns (string memory) {
        str1 = str2;
        bytes(str2)[0] = bytes1("9");
        return str1; // 第一次触发返回 '1234' 第二次触发返回 '9234'
    }

}

存储变量 => 内存变量

  • 值类型:创建一个新的副本
  • 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    uint256 public state1 = 10;
    string public str1 = "ABCD";

    function testA() public returns (uint256) {
        uint256 state2 = 20;
        state2 = state1;
        state1 = 30;
        return state2; // 第一次触发返回10  第二次触发返回30
    }

    function testB() public returns (string memory) {
        string memory str2 = "1234";
        str2 = str1;
        bytes(str1)[0] = bytes1("9");
        return str2; // 第一次触发返回 'ABCD' 第二次触发返回 '9BCD'
    }
    
}

内存变量 => 内存变量

  • 值类型:创建一个新的副本
  • 引用类型:传递指针
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    function testA() public returns (uint256) {
        uint256 state1 = 10;
        uint256 state2 = 20;
        state1 = state2;
        state2 = 30;
        return state1; // 返回值永远是20
    }

    function testB() public returns (string memory) {
        string memory str1 = "ABCD";
        string memory str2 = "1234";
        str1 = str2;
        bytes(str2)[0] = bytes1("9");
        return str1; // 返回值永远是 '9234'
    }

}

内存变量 => 存储变量

  • 值类型:创建一个新的副本
  • 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    uint256 public state1 = 10;
    string public str1 = "ABCD";

    function testA() public returns (uint256) {
        uint256 state2 = 20;
        state1 = state2;
        state2 = 30;
        return state1; // 返回值永远是 20
    }

    function testB() public returns (string memory) {
        string memory str2 = "1234";
        str1 = str2;
        bytes(str2)[0] = bytes1("9");
        return str1; // 返回值永远是 '1234'
    }

}

案例

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Demo {

    string public state1 = "ABC";

    struct MyStruct {
        string name;
        uint8 age;
    }

    mapping(address => MyStruct) public state2;

    constructor() {
        state2[msg.sender] = MyStruct({name: "b00l", age: 18});
    }

    function test1() public returns (string memory, string memory) {
        string memory memory1 = "1234";
        state1 = memory1;
        string storage storage1 = state1;
        bytes(storage1)[0] = bytes1("9");
        return (state1, storage1); // 9234 9234
    }

    function test2() public returns (string memory, string memory) {
        string memory memory1 = "1234";
        state1 = memory1;
        string memory memory2 = state1;
        bytes(memory2)[0] = bytes1("9");
        return (state1, memory2); // 1234 9234
    }

    function test3() external returns (MyStruct memory, MyStruct memory) {
        MyStruct memory memory1 = MyStruct({name: "b00l2", age: 20});
        state2[msg.sender] = memory1;
        MyStruct storage storage1 = state2[msg.sender];
        storage1.age++;
        return (state2[msg.sender], storage1); // b00l2, 21 b00l2, 21
    }

    function test4() external returns (MyStruct memory, MyStruct memory) {
        MyStruct memory memory1 = MyStruct({name: "b00l2", age: 20});
        state2[msg.sender] = memory1;
        MyStruct memory memory2 = state2[msg.sender];
        memory2.age++;
        return (state2[msg.sender], memory2); // b00l2, 20 b00l2, 21
    }

}

calldata 和 memory 的区别

  • calldata 可以隐式地转换为 memory
  • memory 不能隐式地转换为 calldata

本文链接:

https://www.55geek.cn/index.php/archives/126/
1 + 1 =
快来做第一个评论的人吧~