Skip to content

Latest commit

 

History

History
144 lines (98 loc) · 5.5 KB

16 数据位置.md

File metadata and controls

144 lines (98 loc) · 5.5 KB

数据位置

在Solidity中,数据位置非常重要,因为它可以影响变量、函数参数和返回值的行为。本文将详细探讨Solidity中的三种数据位置:storage、memory和calldata。

Storage

Storage是永久性存储位置,存储在以太坊区块链上。在合同中定义的变量、在结构体和数组中的元素及其映射键/值对都在storage中存储。对storage的访问是最昂贵的,并且只能使用整数类型、地址类型、定长数组和结构体。

下面是一个例子,演示了如何在storage中存储和读取变量:

pragma solidity ^0.8.0;

contract Storage {
    uint256 number; // stored in storage
    function setNumber(uint256 _number) public {
        number = _number;
    }

    function getNumber() public view returns (uint256) {
        return number;
    }
}

在这里,number变量存储在合同的storage中。setNumber函数将_uint256_类型的参数存储到number变量中,而getNumber函数从storage中读取number变量的值并返回它。

Memory

Memory是临时性存储位置,它不会持久化在以太坊区块链中。在函数调用期间,函数参数和局部变量都存储在memory中。对memory的访问较便宜,但是只能使用整数类型、地址类型、定长数组、结构体、枚举和字符串。

下面是一个演示如何在memory中存储和读取数据的例子:

pragma solidity ^0.8.0;

contract Memory {
 function doSomething(uint256[] memory values) public pure returns (uint256) {
 uint256 sum;

 for (uint256 i =0; i < values.length; i++) {
 sum += values[i];
        }

        return sum;
    }
}

在这里,函数doSomething接收一个uint256类型的动态数组values。在函数执行期间,values数组存储在memory中。函数计算values数组中所有元素的总和,然后将其作为返回值返回。

Calldata

Calldata是一种特殊的存储位置,它用于存储在函数调用期间传递给合同的参数和数据。与memory相似,calldata的访问较便宜,但是只能使用定长字节数组。

下面是一个演示如何在calldata中读取数据的例子:

pragma solidity ^0.8.0;

contract Calldata {
    function doSomething(bytes memory data) public pure returns (bytes memory) {
        return data;
    }
}

在这里,函数doSomething接收一个bytes类型的参数data。data参数传递给函数在calldata中存储。函数返回接收到的参数data,然后返回它。

结论

在Solidity中,数据位置非常重要。Storage用于在区块链上永久存储数据,它很昂贵,只能使用基本类型和结构体。Memory用于在函数调用期间存储临时数据,它相对便宜,但不能存储动态数组。Calldata用于在函数调用期间传递参数和数据,它相对便宜,但只能使用定长字节数组。了解这些位置对于编写高效和经济的代码至关重要。

扩充

变量声明为存储(Storage)、内存(Memory)或调用数据(Calldata),以显式指定数据的位置。

storage指向存储状态变量。 memory和calldata用于只读参数以避免修改状态。

Storage - 存储在区块链上的数据。存储是持久化的,函数调用结束后数据仍会保留。所有状态变量都存储在这里(变量是一个状态变量(存储在区块链上))

Memory - 存储临时数据,函数调用结束后会被清除。不会持久化存储在区块链上。主要用于存储函数的参数和局部变量。(变量位于内存中,并且在调用函数时存在)

Calldata - 与Memory类似,用于临时存储函数调用参数。但Calldata是只读的,不可修改。Calldata通常用于外部函数调用,可以减少gas fee。(包含函数参数的特殊数据位置)

简单来说:

Storage - 永久存储,存储状态变量

Memory - 临时存储,函数局部变量

Calldata - 临时只读存储,外部函数调用参数

memory和calldata都只是存在于function中的修饰符, struct/mapping/Array这三种类型的变量做名义参数时,需要在前面加上memory或者calldata

当你需要状态持久化时,使用Storage。当你需要临时空间存储数据时,使用Memory。外部函数调用参数建议使用Calldata节省gas。

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

contract DataLocations {
    uint[] public arr;
    mapping(uint => address) map;
    struct MyStruct {
        uint foo;
    }
    mapping(uint => MyStruct) myStructs;

    function f() public {
        // 具有状态变量的 call _ f
        _f(arr, map, myStructs[1]);

        // 从映射中获取结构
        MyStruct storage myStruct = myStructs[1];
        // 在内存中创建结构
        MyStruct memory myMemStruct = MyStruct(0);
    }

    function _f(
        uint[] storage _arr,
        mapping(uint => address) storage _map,
        MyStruct storage _myStruct
    ) internal {
        // 对存储变量进行处理
    }

    // 可以返回内存变量
    function g(uint[] memory _arr) public returns (uint[] memory) {
        // 用内存数组做一些事情
    }

    function h(uint[] calldata _arr) external {
        // 使用 Calldata 数组做一些事情
    }
}

internal关键字的作用是将_f函数设置为内部函数,意味着_f函数只能被contract内的其他函数调用,外部无法调用。

同时要注意: internal函数与private函数不同的是:

  • private函数只能在定义它的contract内调用。
  • internal函数可以在定义它的contract内以及继承该contract的子contract中调用。