Solidity——storage、memory、calldata用法和区别

在Solidity中,storagememory和calldata是用于声明和处理数据的三个关键字,它们具有不同的用途和作用域。

一、storage

storage关键字用于声明永久存储在区块链上的数据,也就是合约的状态变量。使用storage关键字声明的变量将持久保存在区块链上,直到合约被销毁。对于storage变量,数据存储在合约的存储空间中,并且状态的更改将永久记录在区块链上。默认情况下,合约的状态变量是storage类型。

在函数参数中使用storage关键字是无效的,因为storage关键字只用于声明合约的状态变量,而不适用于函数的形参,否则会报错。但在以下情况中必须使用:

//角色库(管理所有角色地址)
// 1. 实现增加角色地址
// 2. 移除角色地址
// 3. 判断角色地址是否被授权
library Roles {
    struct Role {
        mapping (address=>bool) bearer;
    }

    function add(Role storage role,address account) internal {
        require(!has(role,account),"account already has role");
        role.bearer[account] = true;
    }

    function remove(Role storage role,address account) internal {
        require(has(role,account),"account does not have role");
        role.bearer[account] = false;
    }

    function has(Role storage role,address account) internal view returns(bool){
        require(account != address(0),"account is not zero address");
        return role.bearer[account];
    }
}

此处storage关键字用于指定在函数参数中使用的是对状态变量的引用,而不是复制状态变量的值。这样可以直接修改传入的状态变量,而不是创建一个新的副本。

在上述代码中,函数addremovehas中的Role storage参数表示将Role结构体作为引用传递,以便直接修改传入的Role对象。

二、memory

memory关键字用于在函数执行期间暂时存储临时变量,仅在函数执行期间存在。它通常用于存储局部变量和复制从 calldata 中传入的数据。

三、calldata

calldata关键字用于存储函数参数和返回值,是只读的,不能被修改。它通常用于处理外部函数调用传入的数据。在函数内部,你可以使用 calldata 参数进行只读操作,但不能修改它。当你声明函数参数时,默认情况下,它们被认为是 calldata 类型。因此,你通常不需要显式声明参数为 calldata,除非你有特殊的需求。这种默认行为是为了简化 Solidity 代码,因为大多数函数参数都是在 calldata 区域中传递的。显式声明为 calldata 可能会让代码显得冗长,因此 Solidity 使用默认行为,使代码更加简洁。

四、用法

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

contract StorageTest {

    uint256 public data; // 默认为storage类型

    ///storage:storage关键字用于声明永久存储在区块链上的数据,也就是合约的状态变量。
    ///使用storage关键字声明的变量将持久保存在区块链上,直到合约被销毁。
    ///对于storage变量,数据存储在合约的存储空间中,并且状态的更改将永久记录在区块链上。默认情况下,合约的状态变量是storage类型。
    function setData(uint256 _data) external  {
        data = _data;
    }

    ///memory:memory关键字用于声明临时数据,主要用于函数内部的局部变量和函数参数。
    ///memory中的数据只在函数执行期间存在,函数执行完毕后将被清除,不会永久存储在区块链上。在函数之间传递大量数据时,使用memory关键字可以避免消耗过多的燃料(Gas)。
    function concatenate(string memory _a, string memory _b) public pure returns (string memory) {
        bytes memory a = bytes(_a);
        bytes memory b = bytes(_b);
        bytes memory result = new bytes(a.length + b.length);
        
        uint256 k = 0;
        for (uint256 i = 0; i < a.length; i++) {
            result[k++] = a[i];
        }
        for (uint256 i = 0; i < b.length; i++) {
            result[k++] = b[i];
        }
        
        return string(result);
    }

}

在上述示例中,data变量是一个状态变量,其类型为uint256,它存储在合约的存储空间中。

concatenate函数接受两个字符串参数,声明为memory类型,表示这些数据是临时的。在函数内部,我们使用bytes类型来操作字符串,将两个字符串连接起来并返回结果。

五、区别

  • storage用于声明合约的状态变量,数据存储在合约的存储空间中,持久保存在区块链上。
  • memory用于声明函数的局部变量和参数,数据仅在函数执行期间存在,不保存在区块链上。
  • calldata用于存储函数参数和返回值,是只读的,不能被修改。它通常用于处理外部函数调用传入的数据。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tomggo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值