自学内容网 自学内容网

solidity基础 -- 存储类型

        在Solidity智能合约开发中,存储类型是一个至关重要的概念。它不仅影响合约的性能,还决定了数据的存储位置和生命周期。Solidity提供了三种主要的存储类型:storagememorycalldata。本文将结合给定的代码示例,并通过更多实例详细介绍 这三种储存类型的区别。

  • storage:用于合约状态变量,数据持久保存在区块链上,对其修改会影响合约的长期状态。
  • memory:用于局部变量,仅在函数执行期间存在,函数结束后数据消失,可修改,但不会影响 storage 中的数据。
  • calldata:专门用于函数参数,是只读的,存储在调用数据中,在函数执行期间不可变。

 

1. Storage:状态变量与持久化存储

        storage是Solidity中最持久的存储类型,它用于存储合约的状态变量。这些变量会永久存储在区块链上,直到被显式修改或删除。storage变量的生命周期与合约的生命周期相同,这意味着它们在合约部署后一直存在。

        在我们的示例合约中,myStructs是一个storage类型的映射,它存储了每个地址对应的MyStruct结构体。当我们通过myStructs[msg.sender]访问或修改它时,实际上是在操作区块链上的持久化数据。

myStructs[msg.sender] = MyStruct({foo: 123, text: "bar"});

        这行代码将一个MyStruct实例存储到myStructs映射中,它会永久保存在区块链上,直到被覆盖或删除。

使用场景

  • 存储合约的状态变量,如用户余额、配置参数等。

  • 在函数中引用和修改状态变量。

注意事项

  • storage变量的读写操作成本较高,因为它们需要与区块链交互。

  • 修改storage变量会消耗更多的Gas。

2. Memory:临时存储与局部变量

        memory是Solidity中用于临时存储数据的存储类型。它用于存储函数内部的局部变量,这些变量仅在函数执行期间存在,函数执行结束后,存储在memory中的数据会被销毁。

        在示例合约中,readOnly是一个memory类型的变量,它是myStructs[msg.sender]的一个副本。对readOnly的修改不会影响原始的storage变量:

MyStruct memory readOnly = myStructs[msg.sender];
readOnly.foo = 456;

这行代码将myStructs[msg.sender]的内容复制到memory中,并修改了副本的foo属性。然而,这个修改不会反映到链上的storage变量中。

使用场景

  • 存储函数内部的临时变量。

  • 创建数据的副本进行操作,而不影响原始数据。

注意事项

  • memory变量的生命周期仅限于函数执行期间。

  • 创建memory变量需要消耗Gas,但比storage操作更高效。

3. Calldata:函数参数的只读存储

        calldata是Solidity中一种特殊的存储类型,它用于存储函数的输入参数。calldata变量是只读的,不能被修改。它们存储在内存中,但与memory不同,calldata变量不能被分配或重新赋值。

在示例合约中,yscalldata类型的参数:

function examples(uint[] calldata y, string calldata s) external returns (uint[] memory)

这行代码声明了两个calldata参数。y是一个uint数组,s是一个字符串。这些参数在函数调用时由调用者提供,并且在函数内部只能被读取,不能被修改。

使用场景

  • 存储函数的输入参数。

  • 用于传递大量数据,如数组或结构体,而不需要复制到memory

注意事项

  • calldata变量是只读的,不能被修改。

  • calldata变量不能被分配或重新赋值。

 

4.示例合约:DataLocations

在我们开始之前,先来看一个简单的Solidity合约,它展示了这三种存储类型的使用:

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

contract DataLocations {
    struct MyStruct {
        uint foo;
        string text;
    }

    mapping (address => MyStruct) public myStructs;

    function examples(uint[] calldata y, string calldata s) external returns (uint[] memory) {
        myStructs[msg.sender] = MyStruct({foo: 123, text: "bar"});
        
        MyStruct storage myStruct = myStructs[msg.sender];
        myStruct.text = "foo";
        // 修改存储在`storage`中的状态变量

        MyStruct memory readOnly = myStructs[msg.sender];
        readOnly.foo = 456;
        // 修改存储在`memory`中的副本,不影响链上数据

        _internal(y);

        uint[] memory memArr = new uint[](3);
        memArr[0] = 234;
        return memArr;
    }

    function _internal(uint[] calldata y) private {
        uint x = y[0];
    }
}

其他实例:深入理解存储类型

为了进一步理解这三种存储类型的区别,我们再来看一个简单的示例合约:

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

contract StorageTypes {
    uint public storageVar = 10; // 存储在`storage`中的状态变量

    function example() public pure returns (uint memoryVar) {
        uint memory memoryVar = 20; // 存储在`memory`中的局部变量
        return memoryVar;
    }

    function example2(uint calldataVar) public pure returns (uint) {
        return calldataVar; // `calldata`变量,只读
    }
}

总结

  • storage:用于合约状态变量,数据持久保存在区块链上,对其修改会影响合约的长期状态。
  • memory:用于局部变量,仅在函数执行期间存在,函数结束后数据消失,可修改,但不会影响 storage 中的数据。
  • calldata:专门用于函数参数,是只读的,存储在调用数据中,在函数执行期间不可变。

        通过深入理解这三种储存类型的区别,开发者可以更好地优化智能合约的性能和资源使用,确保合约的正确性和稳定性。

 


原文地址:https://blog.csdn.net/Dalik1018/article/details/145311494

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!