区块链研究实验室 | 手把手教你使用Solidity开发智能合约(四)

3月29日

在上篇文章中,我们可以看到的数据位置是如何工作的(传送门:区块链研究实验室 | 使用Solidity开发智能合约(三)),这一篇我们将继续学习Solidity中的变量的过程。这次,我们将重点放在引用类型上,该引用类型应显式指定数据位置,正如我们在前几篇文章中提到的那样。我们还将看到如何定义映射,枚举和常量。


数组

在Solidity中,我们有两种类型的阵列:存储阵列和内存阵列。

1.存储阵列:

这些数组被声明为状态变量,并且可以具有固定长度或动态长度。

可以调整具有动态长度的存储阵列的大小,这意味着它们可以访问push()和pop()方法。

    pragma solidity ^0.7.0;contract A {      uint256[] public numbers;// dynamic length array      address[10] private users; // fixed length array      uint8 users_count;            function addUser(address _user) external {          require(users_count < 10, "number of users is limited to 10");          users[users_count] = _user;          users_count++;      }            function addNumber(uint256 _number) external {          numbers.push(_number);      }}

    2.内存阵列:

    这些数组memory以其数据位置声明。它们也可以具有固定长度或动态长度,但是动态大小的内存阵列无法调整大小(即,不能调用push()和pop()方法)。数组的大小必须预先计算。

    使用new关键字声明动态大小的内存数组,如下所示:

      Type[] memory a = new Type[](size)
        pragma solidity ^0.7.0;contract B {          function createMemArrays() external view {         uint256[20] memory numbers;         numbers[0] = 1;         numbers[1] = 2;                  uint256 users_num = numbers.length;         address[users_num] memory users1; // ERROR : expected integer literal                                           //         or constant expression         address[] memory users2 = new address[](users_num);         users2[0] = msg.sender; // OK         users2.push(msg.sender); // ERROR : member push is not available              }      }

        这里要提到的另一点是关于何时使用内存数组并编写如下内容:

          uint256[] memory array;array[0] = 1;

          您不会收到任何警告,但最终将获得无效的操作码,因为array根据内存中布局的说明,该操作码将指向零插槽,因此切勿将其写入。请记住,在使用数组之前,请务必先对其进行初始化,以便获取有效的地址。

          数组切片

          数组切片只能与calldata数组一起使用,并写为x[start:end]。切片的第一个元素为x[start],最后一个元素为x[end - 1]。

          两个start和end是可选的:start默认为0与end默认为数组的长度。

          特殊的动态尺寸阵列

          1.byte[] 或者 bytes

          这些数组可以保存任意长度的原始字节数据。两者之间的区别在于byte[]遵循数组类型的规则,并且如本部分文档所述, Solidity中的内存数组中的元素始终占据32字节的倍数。这意味着,如果元素的长度小于32字节的倍数,则将对其进行填充,直到其适合所需的大小为止。

          在byte数组的情况下,这将浪费每个元素31个字节,而对于bytes或则不然string。我会提醒您,从内存中读取或写入一个字(32字节)会消耗3气,这就是为什么建议使用bytes而不是的原因byte[]。

          2.string

          string是UTF-8数据的动态数组。与其他语言相反,stringSolidity不提供获取字符串长度或执行两个字符串的串联或比较的功能(需要使用库)。
          可以使用将字符串转换为字节数组bytes(<string>)。这将返回字符串的UTF-8表示形式的低级字节。

          注意:可以将一个字符编码为一个以上的字节,这意味着字节数组的长度不一定是字符串的长度。

          3.字符串与 bytes

          该文档的大多数示例都使用bytes32代替string,并且还很明确地使用值类型bytes1来bytes32限制字符串的字节数,因为它便宜得多。

          结构

          与C和C ++一样,结构允许您定义自己的类型,如下所示:

            struct Donation {      uint256 value;      uint256 date;}

            一旦定义了结构,就可以开始将其用作状态变量或在函数中使用。

            为了初始化一个结构,我们有两种方法:

            1.使用位置参数:

              Donation donation = Donation(msg.value,block.timestamp);

              2.使用关键字:

                Donation donation = Donation(msg.value,block.timestamp);

                第二种方法将避免我们必须记住结构成员的顺序,因此它可能比第一种有用。

                该结构的成员使用点来访问:

                  uint256 donationDate = myDonation.date;

                  “虽然结构本身可以是映射成员的值类型,也可以包含其类型的动态大小的数组,但结构不可能包含其自身类型的成员。这种限制是必要的,因为结构的大小必须是有限的。”

                  对应关系

                  您可以将映射视为大量的键/值存储,其中每个可能的键都存在,并且可以使用该键一键设置或检索任何值。

                  映射声明如下:

                    mapping( KeyType => ValueType) VariableName

                    该KeyType可以是任何内置值类型(我们看到的那些部分1),字节或字符串,或任何合约或枚举类型。的ValueType可以是任何类型的,包括映射,数组和结构。

                    这里要提到的一件事是,映射变量唯一允许的数据位置是storage,这意味着您只能将它们声明为状态变量,存储指针或库函数的参数。

                    枚举

                    枚举可让您将自定义类型下的相关值分组,如以下示例所示:

                      enum Color { green , blue, red }

                      enum使用以下语法可以访问值:

                        Color defaultColor = Color.green;

                        注意:也可以在合同或库定义之外的文件级别上声明枚举。

                        常量和不可变状态变量

                        状态变量可以声明为constant或immutable。在这两种情况下,在构造合同之后都无法修改变量。对于constant变量,该值必须在编译时固定,而对于immutable,它仍可以在构造时分配。

                        编译器不会为这些变量保留存储槽,并且每次出现都会由相应的值替换。

                        常量使用关键字声明constant:

                          uint256 constant maxParticipants = 10;

                          对于不可变状态变量,使用关键字声明它们immutable:

                            contract C {      address immutable owner = msg.sender;      uint256 immutable maxBalance;          constructor(uint256 _maxBalance){           maxBalance = _maxbalance;      }}

                            您可以在本节的文档中找到有关常量和不可变状态变量的更多详细信息。

                            注意:也可以constant在文件级别定义变量。

                            删除关键字

                            我要补充的最后一件事是delete在Solidity中的使用。
                            它用于将变量设置为其初始值,这意味着该语句的delete a行为如下:

                            • 对于整数,它等效于a = 0。

                            • 对于数组,它分配长度为零的动态数组或长度相同的静态数组,并将所有元素设置为其初始值。

                            • delete a[x]删除x数组索引处的项目,并保持所有其他元素和数组长度不变。这尤其意味着它在阵列中留有间隙。

                            • 对于结构,它将为所有成员重置分配一个结构。

                            • delete 对映射没有影响(因为映射的键可能是任意的,并且通常是未知的)。

                            练习时间:简单

                            在本练习中,我们将创建一个用于管理用户的合同。

                            以下是说明:

                            • 创建一个新文件并添加一个名为Crud的合同。

                            • 创建一个名为User的结构,其中包含用户的ID和名称。

                            • 添加两个状态变量并将其公开:1)动态的用户数组;2)每次创建新用户时将增加的id。

                            下一步是创建Crud函数,但是由于我没有向您介绍Solidity函数,因此我将为您提供声明函数的语法。在下一篇文章中,我们将对它们进行详细的讨论:

                              函数function_name(<param_type> <param_name>)<可见性> <状态可变性> [returns(<return_type>)] {...}

                              可见性可以是:公开,私有,内部,外部。
                              状态可变性可以是:查看,纯净,应付。

                              这是您将创建的功能的描述。

                              1.添加

                              可见性:公共
                              状态可变性:空

                              该函数将用户名作为参数,创建一个具有新ID的User实例(每次添加新用户时ID都会自动递增),并将新创建的用户添加到数组中。

                              2.阅读

                              可见性:公共
                              状态可变性:视图

                              此函数获取要查找的用户的ID,如果找到则返回用户名,否则返回用户名(有关稍后的异常处理)。

                              3.更新

                              可见性:公共
                              状态可变性:空

                              此函数将获取用户的ID和新名称,然后在找到相应用户时对其进行更新,或者在不存在该用户的情况下还原该事务。

                              4.销毁

                              可见性:公共
                              状态可变性:空

                              此函数将用户的ID删除,如果找到,则将其从数组中删除;如果用户不存在,则还原事务。

                              提示:由于最后三个函数都需要查找用户,因此您将需要创建一个私有函数,该函数将获取用户的ID并在找到时返回其在数组中的索引,以避免重复相同的代码。

                              课后联系答案:

                                // SPDX-License-Identifier: MITpragma solidity ^0.7.0;contract Crud {        struct User {        uint256 id;        string name;    }        User[] public users;    uint256 public nextId = 1;        function add(string memory name) public {        User memory user = User({id : nextId, name : name});        users.push(user);        nextId++;    }        function read(uint256 id) public view returns(string memory){        uint256 i = find(id);        return users[i].name;    }        function update(uint256 id, string memory newName) public {        uint256 i = find(id);        users[i].name = newName;    }        function destroy(uint256 id) public {        uint256 i = find(id);        delete users[i];    }        function find(uint256 id) private view returns(uint256){        for(uint256 i = 0; i< users.length; i++) {            if(users[i].id == id)                return i;        }        revert("User not found");    }    }

                                至此,我们对变量的讨论结束了。下次,我们将研究功能以及如何在Solidity中使用它们。

                                (0)

                                相关推荐

                                • MATLAB(一)——软件及基本操作介绍

                                  一.MATLAB软件介绍 1.matlab的界面 左上角,home标签下,找到layout进行设置/复位,可以设置各板块的显示与隐藏.其中有几个部分,请务必要显示 ①Current Folder:中文 ...

                                • 如何使用Truffle部署智能合约 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 今天 Ťruffle是一个开发环境(提供用于编译,部署,测试和构建的命令行工具),框架(提供各种程序包以使其易于编写测试,部署代码,构建客户端等)和资产管道(发布软件包 ...

                                • 创建NFT所需的知识 | 区块链研究实验室

                                  链三丰 区块链研究实验室 今天 与DeFi代币和初始代币发行(ICO)一样,不可替代的代币或NFT是新的加密热潮.尽管短期内大肆宣传,但一些艺术家和评论家认为NFT确实具有长期可行性. 实际上,NFT ...

                                • 应用程序Python的日志记录模板 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 4天前 通过定义明确,信息量大且结构方便的日志,调试和跟踪应用程序执行的理想方式. 它们是任何编程语言(不仅仅是Python)中任何大型,中型或大型项目必不可少的组成部 ...

                                • 分散式应用程序的简单解释| 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 今天 在本文中,我们将向大家解释什么是分散式应用程序(DApp)以及它的工作原理. 什么是DApp? 分散式应用程序(DApp的缩写)在前端可视化并在对等网络上执行的智 ...

                                • 如何在区块链上部署链码 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 今天 介绍 在本文中,我们将部署JAVA,GO,JAVASCRIPT的链代码以运行Hyperledger Fabric区块链网络. 网络设置 具有一个应用程序通道的Hy ...

                                • 区块链技术的开发简介 | 区块链研究实验室

                                  今天 概述 本文将向大家提供有关区块链技术开发入门时应该了解的一切的简要概述,在这里给大家展示的是学习区块链应用程序开发的基础知识,由区块链提供支持的去中心化应用程序(Dapps). 有许多不同的区块 ...

                                • 如何在TRON区块链上查询交易 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 今天 乙锁链交易是两个或多个地址之间交互的记录. 在TRON区块链上,通常有两个交互地址,并且它们之间的交互可以采用许多不同的形式(例如,创建新帐户或资产,触发智能合约 ...

                                • 如何构建区块链REST服务器 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 昨天 当前,区块链正在风靡一时,区块链真正的魔力是通过区块链应用于不同行业和领域而实现的众多可能性. Hyperledger Foundation一直在创建一个属于自己 ...

                                • 如何使用Python异步编程进行API调用 | 区块链研究实验室

                                  原创 链三丰 区块链研究实验室 今天 收录于话题 #Python1 #区块链技术33 #区块链44 #API1 #区块链应用30 本文中,将向大家介绍如何使用Python异步编程,以便您可以更快地进行 ...