# Web3.0学习树|5.Solidity基础 Ⅱ **Published by:** [链上巫师](https://paragraph.com/@looplove/) **Published on:** 2022-11-25 **URL:** https://paragraph.com/@looplove/web3-0-5-solidity ## Content 这是Web3.0学习的第二章Solidity基础,文内如出现知识性和理解性错误还请各位斧正,原创文章,完全开源,若能得转载不胜荣幸,推特会分享Coding学习,生活日常和NBA等内容。**目录Solidity基础函数数组和结构体错误和警告变量/常量/ImmutableSolidity基础函数"函数"或者"方法"指的是独立模块,在我们调用的时候会执行某些指令,Solidity的函数性质和Java一样,函数通过"function"关键字表示。 为了查看一个函数的实际运行结果,我们需要把合约部署在一个测试环境上,把它部署在本地网络,或者叫javascript VM(现在称其为Remix VM),在部署之前,我们需要看看它有没有正确编译。 Remix VM表示合约将被部署到本地的Remix虚拟机上,Remix VM是本地测试用的区块链,在上面可以快速模拟交易,不需要等待测试网的流程。 运行Remix VM的时候,我们有很多账户可以部署,每个账户中都有100个以太币,这些账户和Metamask中的账户类似,区别是,这些是在Remix VM中的测试以太币。 在部署合约的交易中,可以设置Gas limit,同时可以选择要部署的合约在测试环境中,合约也有一个地址,每个合约都有一个地址,就像每个钱包账户都有地址一样。部署一个合约其实就是发送一个交易(任何时候你改变链上的任何东西,包括制定一个新的合约,它都会发生在一个交易中),部署一个合约就修改了区块链,让链上拥有这个合约参数的访问级别如果没有特别指定的话默认是internal. 将参数的访问级别设置为public后的合约: favoriteNumbre按钮,类似一个函数,显示变量的值。 这些按钮都是被因代码语句而被Solidity自动赋予的视图功能。权限声明: 函数分为不同的可见性,用户使用不同的关键字进行声明:public:在外部和内部都可见,任何与合约交互的人,都可以看到favoriteNumber中存的值(public会创建storage(存储)和state(状态)变量的getter函数)给favoriteNumber加上public,实际上就是给favoriteNumber创建了getter函数,我们其实创建了一个函数,返回favoriteNumber的值,上图中的蓝色按钮就是一个返回favoriteNumber值的函数。 带有public的变量favoriteNumber,可以看作是一个返回uint256的view函数private:表示只有这个合约可以调用这个函数,对于Storage来说,它不是说只有这个合约才能读取它的值。private表示这个合约是唯一可以调用favoriteNumber函数的合约。private表示只对合约内部可见。external:表示只对合约外部可见,表示合约外的账户可以调用这个函数。internal:表示只有这个合约或者继承它的合约可以调用。智能合约的函数越复杂,消耗的计算量就越多,我们花掉的Gas费就越多(The more "stuff" in your function the more gas it costs) 查询数据的合约函数也有不同的声明方式:view可以读取变量,但不能修改pure不可以读也不可以修改Scope(作用域) favoriteNumber是在Global scope(全局作用域)中,表示所有在花括号中的任何函数都可以获取它。 当你创建一个变量的时候,它只有在这个作用域(花括号)才可见 因为something函数不在store函数中,所以something函数无法获取testVar变量。 这就是作用域的原理,看变量是否在这个花括号中被创建,然后判断是否可以被别的函数获取标识函数的调用不需要消耗Gas,Solidity中有两个可以把普通函数转换为标识函数的关键字,这两个关键字是view和pure 如果一个函数是view函数,意味着我们只会读取这个合约的状态 例如:retrive函数只读取favoriteNumber的值 view函数和pure函数不允许修改任何状态,但是pure函数也不允许读取区块链数据(所以我们也不能读取favoriteNumber的值)通过pure函数,你想做的事情可能是这样的,在pure函数中,返回1+1的结果(类似于这样的东西,可能是常用的方法,或者某个不需要读取数据的算法)。为什么我们调用view或者pure函数,是不需要支付Gas的? 因为只是读区块链数据,记住,只有更改状态的时候才支付Gas。 但是如果一个要改变区块链状态的函数调用了类似retrive这种view或者pure函数才会消耗Gas 假设:store不是view函数,它在某处调用了retrive,那它就要支付retrive的Gas。 关键字return表示,我们调用函数后会得到什么类型的数据。数组和结构体数组是一种存储同类元素的有序集合,通过uint[] public arr;来进行定义,在定义时可以预先指定数组大小,如uint[10] public myFixedSizeArr;。 需要注意的是,我们可以在内存中创建数组,但是必须固定大小,如uint[] memory a = new uint 数组类型有一些基本操作方法,如下://定义数组类型 uint[7] public arr; //添加数据 arr.push(7); //删除最后一个数据 arr.pop(); //删除某个索引值数据 delete arr[1]; //获取数组长度 uint len = arr.length; 示例://SPDX-License-Identifier:MIT pragma solidity ^0.8.8; contract SimpleStorage { uint256 public favoriteNumber; function store(uint256 _favoriteNumber) public { favoriteNumber = _favoriteNumber; uint256 testVar = 5; } //创建一个函数来返回favoriteNumber,模拟被自动创建的getter函数 //view,pure function retrieve() public view returns(uint256){ return favoriteNumber; } function add() public pure returns(uint256){ return(1+1); } } 之前我们的合约允许我们存储一个喜欢的号码,但是如果我们想存储一系列最喜欢的数字,或者我们想存储一大群不同的人,每个人都有不同的所喜欢的数字,该如何去存储呢? 方法引入:每当你有一个变量列表在一个对象内部时,在Solidity中,它们会自动编入索引。 创建列表的更好方法是使用结构类型,称为Struct,Struct是一种存储列表或一系列对象的方式 Struct struct是结构类型,对于复杂业务,我们经常需要定义自己的结构,将关联的数据组合起来,可以在合约内进行定义。 各类操作:contract Struct { struct Data { string id; string hash; } Data public data; //添加数据 function create(string calldata _id) public { data = Data{id: _id, hash:"111222"}; } //更新数据 function updata(string _id) public { //查询数据 string id = data.id; //更新 data.hash = "222333" } } 也可以分离出一个文件定义所有需要的结构类型,由合约按需导入// 'StructDeclaration.sol' struct Data { string id; string hash; } // 'Struct.sol' import "./StructDeclaration.sol" contract Struct { Data public data; } 示例代码: //创建一种新的类型 //有点像uint256/boolean/string struct People { uint256 favoriteNumber; string name; } // uint256[] public favoriteNumberList; //People[3] public people;//静态数组(指定数组大小,该数组中只能放下三个人) People[] public people;//动态数组(数组的大小没有给出,我们不给它一个尺寸,这意味着它可以是任何尺寸,并且数组的大小可以随着我们加减人而增长和缩小) //0: 2,Patrick | 1: 7,Jon | function addPerson(string memory _name, uint256 _favoriteNumber) public{ People memory newPerson = People({favoriteNumber: _favoriteNumber, name: _name}); people.push(newPerson); //上面那两句的效果相当于下面这一句 //people.push(People(_favoriteNumber, _name)); } 错误(红色) 预期得到";"但是实际得到了"}"意味着你的代码没有编译。这些错误意味着你的代码没有编译,它不像预期的那样工作警告(黄色) 警告不会阻止您的代码工作,但检查它们通常是个好主意,因为它们通常会给出很有见地的信息。 SPDX 许可证未提供标识符变量/常量/Immutable变量是Solidity中可改变值的一种数据结构,分为以下三种:local变量state变量global变量其中,local变量定义在方法中,而不会存储在链上,如string var = "Hello"; state变量在方法之外定义,会存储在链上,通过string public var;定义变量,写入值时会发送交易,而读取值则不会; global变量则是提供了链信息的全局变量,如当前区块时间戳变量,uint timestamp = block.timestamp; 合约调用者地址变量,address sender = msg.sender;等。 变量可以通过不同关键字进行声明,表示不同的存储位置。storage,会存储在链上memory,在内存中,只有方法被调用的时候才存在calldata,作为调用方法传入参数时存在而常量是一种不可以改变值的变量,使用常量可以节约Gas费用,我们可以通过string public constant MY_CONSTANT = "0707";来进行定义。immutable则是一种特殊的类型,它的值可以在constructor中初始化,但不可以再次改变。灵活使用这几种类型可以有效节省Gas费并保障数据安全。 ## Publication Information - [链上巫师](https://paragraph.com/@looplove/): Publication homepage - [All Posts](https://paragraph.com/@looplove/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@looplove): Subscribe to updates - [Twitter](https://twitter.com/hanjin_long): Follow on Twitter