solidity中的mapping以及Memory,Storage Calldata

news2024/11/24 15:01:15

1.Memory,Storage & Calldata

在 Solidity 中,有以下几种数据存储位置:

  1. 栈(Stack):栈是一种临时存储区域,用于存储局部变量和函数参数。在函数执行期间,栈上的数据会被分配和释放,当函数执行完成时,栈上的数据也会被销毁。
  2. 内存(Memory):内存是一种临时存储区域,用于存储动态分配的数据,比如动态数组和字符串。与栈不同,内存中的数据不会随着函数执行的结束而销毁,需要手动清除。在函数调用期间,内存中的数据可以被读取和修改。
  3. 存储(Storage):存储是永久存储在区块链上的位置,用于存储合约的状态变量。存储中的数据会一直保存在区块链上,直到合约被销毁。存储是最昂贵的一种存储位置,因为它需要永久存储在区块链上,并且对存储操作收费。
  4. 调用数据(Calldata):调用数据是用于存储外部函数调用的参数和返回值的位置。在函数调用期间,输入参数会被复制到调用数据中,函数执行完成后,返回值也会被写入调用数据中。
  5. 代码(Code):代码用于存储合约本身的字节码,即合约的函数实现、逻辑等内容。
  6. 日志(Logs):日志用于记录合约的事件和状态变化,可以通过日志来实现合约的事件通知和审计功能。

本章节三个最重要的,就是Calldata,Memory和Storage,这是一个稍微进阶的知识点,所以,如果你第一次没有完全掌握它,那也完全没关系。

Storage

 定义:storage是合约状态变量的默认存储位置。这意味着状态变量存储在区块链上,并在整个合约的生命周期内持续存在。

 特点:

  1.    数据在函数调用之间持续存在。
  2.    对storage变量的修改会直接反映在合约的状态中。
  3.    storage变量是可变的,可以在函数中修改。
  4.    访问storage变量通常比访问memory或calldata变量消耗更多的gas。

 使用:状态变量自动存储在storage中,不需要显式指定。

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

contract SimpleStorage {
  uint[] public numbers;//默认存在storeage
   
  function store(uint number) public {
    numbers.push(number);//修改storage中的状态变量
  }

}

memory

定义:memory是函数内部变量的默认存储位置,用于存储临时数据。

 特点:

  1.    数据仅在函数执行期间存在,函数执行结束后数据会被清除。
  2.    memory变量是可变的,可以在函数中修改。
  3.    memory变量的赋值是独立的,对memory变量的修改不会影响storage中的数据。
  4.    访问memory变量比访问storage变量消耗的gas要少。

 使用:当需要在函数中创建结构体、数组或映射的临时副本时,需要显式指定memory。

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

contract MemoryExample {
    struct Person {
        string name;
        uint age;
    }

    function createPerson(string memory name, uint age) public pure returns (Person memory) {
        Person memory person = Person(name, age); // 创建 memory 中的结构体实例
        return person;
    }
}

Calldata 

 定义:calldata是外部函数参数的默认存储位置,用于存储函数调用的输入数据。

 特点:

  1.    数据仅在函数执行期间存在,与memory类似。
  2.    calldata是不可修改的,即不能在函数内部修改calldata变量。
  3.    使用calldata可以节省gas,因为它不需要复制数据。

 使用:通常用于外部函数的参数,特别是当参数是大型数组或结构体时。

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

contract MemoryExample {
   function process(uint[] calldata data) external pure returns(uint sum){
    for(uint i=0;i<data.length;i++){
        sum+=data[i];//直接在calldata中读取数据
    }
   }
}

 总结

  1.  storage用于合约的状态变量,数据在函数调用之间持续存在。
  2.  memory用于函数内的临时变量,数据仅在函数执行期间存在。
  3.  calldata用于外部函数的输入参数,数据在函数执行期间存在,且不可修改。

正确使用这些数据位置关键字可以优化合约的性能和减少gas消耗。

2.如何选择这三个存储方式

在Solidity中,选择使用storage和memory主要取决于数据的使用场景和生命周期。以下是一些指导原则来帮助你决定何时使用storage和memory:

 使用 storage 的情况:

  • 1. 状态变量:合约的状态变量默认存储在storage中,并且应该在合约的生命周期内持久存在。
  • 2. 持久化数据:当你需要保存数据以供合约的多个函数调用之间使用时,应该使用storage。
  • 3. 大数组或映射:如果你需要频繁地更新大数组或映射,将这些数据存储在storage中会更加高效,因为每次修改都会直接发生在区块链的状态上。

 使用 memory 的情况:

  • 1. 临时变量:当你在函数中需要创建临时变量,并且这些变量不需要在函数调用之间保留时,应该使用memory。
  • 2. 函数参数和返回值:对于需要传递给函数的复杂类型(如数组、结构体)的参数,应该使用memory。同样,如果你要从函数返回一个复杂类型,也应该使用memory。
  • 3. 节省 gas:对于不需要持久化的数据,使用memory可以节省gas,因为不需要写入区块链的状态。

 选择 storage 或 memory 的具体步骤:

1. 确定数据的使用频率:

   - 如果数据需要在多个函数调用之间共享,使用storage。

   - 如果数据仅在一个函数调用中需要,使用memory。

2. 考虑数据的持久性:

   - 如果数据需要在合约的生命周期内持续存在,使用storage。

   - 如果数据仅需要临时存储,使用memory。

3. 考虑成本和效率:

   - 如果数据量大,频繁地写入storage会消耗大量gas,考虑是否可以优化数据结构或使用memory来减少成本。

   - 对于大型数据结构,如果不需要修改,使用calldata(对于外部函数的参数)可以进一步节省gas。

4. 考虑数据是否需要修改:

   - 如果需要在函数内部修改数据,使用memory(因为calldata是不可修改的)。

   - 如果数据在函数调用中不需要修改,可以使用calldata作为外部函数的参数。

以下是一个简单的示例,说明如何选择storage和memory:

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

contract MemoryExample {
    //状态变量
    struct User{
        string name;
        uint age;
    }
    User[] public users;

    //将新的用户添加到状态变量中
    function addUser(string memory name,uint age)public{
        User memory newUser =User(name,age);
        //将临时变量复制到storage中的状态变量
        users.push(newUser);
    }

        //处理用户数据,返回一个总和
      function sumUserAges() public view returns(uint) {
            uint sum =0;
            //在memory中创建一个临时数组副本
            User[] memory usersCopy =users;
            for(uint i=0;i<usersCopy.length;i++){
                sum+=usersCopy[i].age;
            }
            return sum;
        }
    
}

 在这个例子中,users是一个状态变量,存储在storage中。函数addUser接收一个memory参数,并创建一个memory中的User结构体实例,然后将其添加到storage中的users数组中。函数sumUserAges创建了一个memory中的数组副本,以避免直接在storage上操作,从而节省gas。

 Mappings

1.Mapping

在Solidity中,mapping是一种可以被视为哈希表的键-值数据结构,它能够以非常高效的方式存储和查找数据。映射在Solidity中是非常有用的,因为它们允许你以O(1)的时间复杂度存储和检索键对应的值,而不需要遍历整个数据结构。

 基本概念

  • - 键(Key):映射中的键可以是任何除了映射、动态数组、合约、枚举和结构体以外的类型。这包括基本类型如uint、address、bytes32等。
  • - 值(Value):映射中的值可以是任何类型,包括映射本身。

 声明映射

映射的声明格式如下:

solidity
mapping(keyType => valueType) public mappingName;

例如,以下是如何声明一个将address映射到uint的映射:

solidity
mapping(address => uint) public balances;

这个balances映射可以被用来跟踪每个地址的余额。

 访问映射

映射的值可以通过将键放入方括号中来访问和修改。如果映射中不存在该键,则其值默认为类型的初始值(例如,数字类型的初始值为0,布尔类型的初始值为false)。

以下是如何设置和检索映射中的值:

solidity
function setBalance(address _addr, uint _value) public {
    balances[_addr] = _value;
}
function getBalance(address _addr) public view returns (uint) {
    return balances[_addr];
}

 映射的特点

  1. - 不存在长度或键的集合:由于映射的设计是为了优化查找效率,因此它不支持返回映射中的所有键或值的集合,也不支持获取映射的长度。
  2. - 存储位置:映射总是存储在storage中,不能在memory或calldata中声明映射。
  3. - 初始化:映射不需要显式初始化,因为所有键的值默认为初始值。
  4. - 可递归:映射可以作为值或键的类型,例如,你可以创建一个映射的映射。
  5.  映射的限制
  6. - 不能迭代:你不能遍历映射的所有键或值,因为没有提供获取所有键的方法。
  7. - 不能删除键:虽然你可以将映射中的值设置为其类型的初始值,但这并不等同于删除键。键仍然存在于映射中,并且其值可以被重新设置。

 示例:使用映射的简单合约

以下是一个简单的合约示例,它使用映射来跟踪地址的余额:

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

contract MemoryExample {
   mapping(address => uint) public balances;

   function updateBalance(uint newBalance) public {
    balances[msg.sender] =newBalance;
   }
}

在这个合约中,任何调用updateBalance函数的地址都可以设置自己的余额。由于映射的键是address类型,所以每个地址都可以有一个唯一的余额。

映射是Solidity中非常有用的数据结构,特别是当你需要快速查找和更新与特定键相关联的值时。然而,由于它们的限制(如无法迭代),在设计合约时,需要仔细考虑是否使用映射。

接下来让我来看一个详细的例子

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

contract SimpleStorage {

    // This gets initialized to zero!
    // <- This means that this section is a comment!
    uint256 public favoriteNumber;

    mapping(string => uint256) public nameToFavoriteNumber;

    struct People {
        uint256 favoriteNumber;
        string name;
    }

    // uint256[] public favoriteNumbersList;
    People[] public people;

    function store(uint256 _favoriteNumber) public {
        favoriteNumber = _favoriteNumber;
        retrieve();
    }

    // view, pure
    function retrieve() public view returns(uint256) {
        return favoriteNumber;
    }

    // calldata, memory, storage
    function addPerson(string memory _name, uint _favoriteNumber) public {
        people.push(People(_favoriteNumber, _name));
        nameToFavoriteNumber[_name] = _favoriteNumber;
    }
}

当你创建一个映射时,会把所有东西都初始化为空值,现在这里每一个可能的字符串,都有一个对应的初始值favoriteNumber为0。

所以我们要手动添加值,就利用我们的addPerson()函数,添加一个人到我们的映射中,等待这个交易确实完成了,然后,让我们多添加几个人。

现在,如果我们查找某个人最喜欢数字,将会立即得到结果,当然我们也可以在people数组中找到它们。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2211154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索光耦:光耦——电动自行车安全与智能的坚实保障

随着电动自行车市场的蓬勃发展&#xff0c;如何提升其安全性、可靠性和智能化水平已成为行业关注的焦点。在众多关键元件中&#xff0c;光电耦合器&#xff08;简称光耦&#xff09;正以其独特的功能&#xff0c;成为电动自行车设计中的关键角色。下面&#xff0c;让我们一同探…

Ubuntu22.04阿里云服务器 Gitlab搭建CICD

gitlab搭建cicd流水线教程 1、阿里云申请免费云盘 申请免费云盘用于创建gitlab 申请方法百度 2、安装gitlab-ce 更新系统&#xff1a; sudo apt update sudo apt upgrade -y 安装必要的依赖&#xff1a; sudo apt install -y curl openssh-server ca-certificates pos…

【云原生】Helm资源清单管理工具

资源清单管理工具-Helm 文章目录 资源清单管理工具-Helm资源列表基础环境一、Helm的介绍1.1、Helm的价值概述1.2、Helm的关键名词 二、安装部署Helm2.1、解压安装包2.2、添加命令补全设置 三、使用Helm部署服务管理3.1、使用Helm创建chart3.2、响应式创建名称空间3.3、安装char…

基于Arduino的植物状态监测系统

Arduino植物监测/浇水系统 本项目的3D打印及源码开源&#xff0c;可以私信我进行获取 简介 大家好&#xff0c;今天我将向大家介绍一个非常有趣的项目——Arduino植物监测/浇水系统。这个项目利用一些传感器来观察土壤的状况&#xff0c;并根据这些读数来判断植物是否需要浇…

异构环境下统一授权管理系统的兼容性具体如何实现?

在异构环境中&#xff0c;由于不同系统的差异性&#xff0c;实现统一授权管理面临诸多挑战。其中&#xff0c;兼容性问题是关键之一。兼容性的实现不仅关系到不同系统之间的协同工作&#xff0c;还直接影响到整个管理系统的效率和稳定性。 异构系统带来的挑战 异构系统的存在…

手写mybatis之通过注解配置执行SQL语句

前言 可能领导也都觉得可能就是码农不爱说话&#xff0c;其实不爱说话是一方面&#xff0c;但还有另外一方面是有些领导对于码农提出的问题&#xff0c;给出的回复往往是&#xff1a;“你提出这个问题&#xff0c;你就要给出这个问题的解决办法&#xff01;” 所以不同的岗位要…

AD24之铺铜操作

1.选择板框&#xff0c;即机械1层&#xff0c;转换为覆铜 这样顶层就铺好了&#xff0c;还需要铺底层 2.打开底层&#xff0c;选择板框&#xff0c;转换为铺铜&#xff0c;然后给铜皮添加网络和层&#xff0c;最后是铺铜 注意&#xff1a;None铺铜是无效果的&#xff0c;要Ha…

2.使用 Label Studio 标注文本

使用 Label Studio 标注文本 文章目录 使用 Label Studio 标注文本前言Label Studio的简单使用1.创建项目2.添加本地存储3.选择标注模板4.添加数据5.标注6.添加关系 总结 前言 Label Studio是一个开源的功能强大的标注平台&#xff0c;可以标注视频&#xff0c;图片&#xff0…

一个新韭菜的炒股心得

一个新韭菜的炒股心得 前言 股市其实是一场修行。时刻控制人性的弱点。所以量化优势明显&#xff0c;它没有情绪&#xff0c;可以随意止盈止损。我从一个小白一路走过来&#xff0c;发现A股里有学不完的知识,有做不完的功课。我的主要关注点在如何有效实现价值投资(价值投资在…

算法: 位运算题目练习

文章目录 位运算判定字符是否唯一丢失的数字两整数之和只出现一次的数字 II消失的两个数字常见位运算总结 位运算 判定字符是否唯一 有很多解法,比如hash表,或者给字符串排个序,然后遍历… 写这道题时没注意到如果出现奇数个相同字符,此时就应该返回false了. 而不是全部放到位…

智慧高铁站的概念与优势

1. 概念介绍 智慧高铁站是指利用先进的信息技术和智能化手段&#xff0c;对高铁站的运营管理、服务功能、安全保障等方面进行全面升级和优化的现代化交通枢纽。通过数字化、网络化和智能化技术的应用&#xff0c;实现高铁站的智能化管理、便捷化服务和可持续发展&#xff0c;从…

【Linux】Linux进程基础

1.进程介绍与概念 进程的本质是在计算机内存中运⾏的程序&#xff0c;但是这⼀个概念太过于⼴泛 每个应用程序运行于现代操作系统之上时&#xff0c;操作系统会提供一种抽象&#xff0c;好像系统上只有这个程序在运行&#xff0c;所有的硬件资源都被这个程序在使用。这种假象…

ui自动化知识点-web端

UI &#xff1a; User Interface( ⽤户接⼝ - ⽤户界⾯ ) &#xff0c;主要包括&#xff1a; app 、 web ui ⾃动化测试&#xff1a;使⽤⼯具或代码执⾏⽤例的过程 什么样的项⽬适合做⾃动化&#xff1a;1、需要回归测试项⽬&#xff08;甲⽅⾃营项⽬、⾦融、电商&#xff09…

鸿蒙开发 三十七 ArkTs类 class 构造函数

语法格式如下&#xff1a; class 类名{ 字段1:类型 字段2:类型 constructor(参数...) { this.字段 参数 } } 实例化&#xff1a; const 实例1 new 类名(参数...&#xff09;每new一次就去执行一次constructor方法&#xff0c;创建不同的实例。 每次new都要传参数&…

MySQL学习笔记(持续更新,目前到十一章锁)

1、Mysql概述 1.1 数据库相关概念 三个概念&#xff1a;数据库、数据库管理系统、SQL 名称全称简称数据库存储数据的仓库&#xff0c;数据是有组织的进行存储DataBase&#xff08;DB&#xff09;数据库管理系统操纵和管理数据库的大型软件DataBase Mangement System&#xf…

C++ -string -常见用法1

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 文章目录 &#x1f4a1;前言1.构造函数1.1函数原型1.2用法速览1.3详解() -重点⭐( s) -重点⭐( n, c) -重点⭐( str) -重点⭐( str, pos, len)( s, n) 2.容量函数2.1函数原型2…

github 上将 stable 合并到 master 分支步骤

本地仓库分支&#xff1a;origin 远端仓库分支&#xff1a;upstream 切到非 master 分支上&#xff0c;比如 dev # 本地操作 git branch -D master git fetch upstream master::master git checkout master # 这步是拉取远端 stable 到 master 上&#xff0c;可能会出错误 # fa…

21次惊艳亮相!凯特王妃的秋季时尚造型!在时尚领域她已经无限接近戴安娜王妃

虽然凯特米德尔顿正式成为英国王室成员才刚刚十多年&#xff0c;但很难想象没有她的王室会是什么样子。毕竟&#xff0c;凭借她非凡的风格和不可否认的魅力&#xff0c;威尔士王妃在2011年迅速赢得了我们以及全世界的喜爱。 我们都喜欢关注她的项目以及她和威廉王子的爱情故事…

基于STM32的智能电能表设计

引言 本项目设计了一个基于STM32的智能电能表&#xff0c;能够实时测量电压、电流、功率以及累计的电能消耗。通过ADC模块采集电压和电流信号&#xff0c;结合功率计算算法&#xff0c;系统可以精准地监控家庭或工业设备的电能消耗。该智能电能表还支持远程数据传输和本地数据…

Krea.ai正式发布AI视频集合站:Luma、Runway与可灵的深度整合

引言 在数字内容日益丰富的今天&#xff0c;视频制作的需求呈现爆发式增长。为了满足创作者对高效、创新视频工具的渴求&#xff0c;Krea.ai推出了全新的AI视频集合站。此平台整合了多款领先工具&#xff0c;如Luma、Runway、可灵和Minimax&#xff0c;为用户提供一站式的视频…