Solidity基础二

半山腰总是最挤的 你得去山顶看看

目录

 一、Solidity的条件语句

 1.if语句

 2.if....else语句

3.if....else if 语句

二、 Solidity的循环语句

1.while循环语句

 2.do...while循环语句

3.for循环语句 

4.控制语句

三、Solidity的函数

1.Solidity函数的基本使用

2.Solidity函数的作用 

3.Solidity函数的参数

4.Solidity函数的调用

5.函数命名参数

6.函数的权限修饰符

external函数

public函数

internal函数

private函数

7.函数的类型修饰符

pure类型函数

view类型函数

         payable类型函数 

0.5.0之后淘汰的一种类型函数 

8.函数的返回值

9.接收(保存)函数的返回值

10.函数重载

11.Solidity的系统函数-数学函数

12.Solidity的系统函数-加密函数      

 四、函数进阶--函数修改器

1. _ 的作用

2. 基本的函数修改器 

3. 带参数的函数修改器

4.修改器和函数里面的return 

5.多个修改器同时修饰一个函数

6.复杂示例

五、函数进阶--函数构造器

1.不带参数的构造函数

2.带参数的构造函数

3.构造函数通常的使用思想


 一、Solidity的条件语句

Solidity支持条件语句,让程序可以根据条件执行不同的操作。条件语句包括:

  • if
  • if...else
  • if...else if....

 1.if语句

if (条件表达式) {
   被执行语句(如果条件为真)
}

 2.if....else语句

if (条件表达式) {
   被执行语句(如果条件为真)
} else {
   被执行语句(如果条件为假)
}

3.if....else if 语句

if (条件表达式 1) {
   被执行语句(如果条件 1 为真)
} else if (条件表达式 2) {
   被执行语句(如果条件 2 为真)
} else if (条件表达式 3) {
   被执行语句(如果条件 3 为真)
} else {
   被执行语句(如果所有条件为假)
}

 温馨提示:solidity里面只有if分支语句,没有switch分支语句

二、 Solidity的循环语句

与其他语言类似,Solidity语言支持循环结构,Solidity提供以下循环语句。

  • while
  • do ... while
  • for
  • 循环控制语句:break、continue。

1.while循环语句

while (表达式) {
   // 如果表达式的结果为真,就循环执行以下语句
   ......
}

 2.do...while循环语句

do {
// 如果表达式的结果为真,就循环执行以下语句
   ......
} while (表达式); 

3.for循环语句 

for (初始化; 测试条件; 迭代语句) {
   // 如果表达式的结果为真,就循环执行以下语句
   ......
}

4.控制语句

Solidity 循环语句支持 continue 和 break,用来改变循环流程。

  • continue – 跳出本次循环,继续执行下一次循环
  • break – 跳出循环,不再执行当前循环

三、Solidity的函数

函数是一组可重用代码的包装,接受输入,返回输出。

Solidity 支持函数定义和调用。

在solidity中函数是一种类型,是值类型

1.Solidity函数的基本使用

关键字是:

function

函数定义格式:

function 函数名 (入参)  函数修饰符 returns(出参){  

        函数体

}

2.Solidity函数的作用 

与区块链交互的手段、封装、复用

3.Solidity函数的参数

参数可以有多个

调用函数传递的参数是实参,进入函数里面的是形参,也叫入参,returns后面的参数出参

为了区分参数,通常把入参名前面加个_ 

示例:

uint unm=1;

function setValue (uint _num , uint _num2) public returns(uint,uint){

        unm=_unm;

        return (num,_num2)

}

4.Solidity函数的调用

  • remix调用:合约部署后直接在remix调用     方法: remix中点击对应的函数名即可,有参数传参数
  • 合约内代码调用(通常是在函数内调用)     格式 :   函数名(参数列表)  

5.函数命名参数

函数调用可以使用命名参数。将参数名和参数值这样的键值对以任意顺序放进json表达式即可,或按顺序录入

好处:可以指定为某个参数赋值 ,不用按照参数原本的排序

格式:

函数名( 该函数的形参值  ,  该函数的形参值  ,······ } )

 

uint unm=1; 

string test ;

function setValue (uint _num , string memory _test) public {

        unm=_unm;

        test =_test;

}

function testSet() public {

setValue(3,”www”);  //这样一定要按顺序

setValue( { _test:”aaaa”,_num:3 } );  //这样可以颠倒顺序

}

6.函数的权限修饰符

  • external外部的 
  • public 公开的
  • internal 内部的
  • private 私密的(封装) 

external函数

外部函数是合约接口的一部分,仅外部访问(在内部也必须用外部访问的方式访问)

一个外部函数f不能通过内部的方式来发起调用,只能以外部形式:this.f()来调用。需要注意的是solidity的this和其他语言不一样

外部函数在接收大的数组数据时更加有效

public函数

public修饰的函数既允许以internal 的方式调用,也允许以external的方式调用

public 修饰的函数,任何账户或者合约都能调用和访问

public 的函数由于被外部合约访问,也属于是合约对外接口的一部分

internal函数

仅当前合约及所继承的合约,只允许以内部的方式调用,子合约也能调用

private函数

private修饰函数,只能在其所在的合约中调用和访问,即使作为父,子合约也无权访问

即使声明为private ,仍能被任何人查看到里面的数据,访问权限只是阻止了其他合约访问函数或者修改数据

7.函数的类型修饰符

  • pure
  • view 

温馨提示:想要修改状态,则不加类型修饰符,即可

支付类型修饰符:

  • payable

0.5.0之后淘汰的一种类型函数 

constant(新版本淘汰,旧版本才有)  了解一下

声明为constant修饰的函数和view是等价的,constant实际上是view的别名

在solidity  0.5.0之后被弃用

pure类型函数

函数声明为pure,表示该函数既不能读取状态,也不能修改状态

使用pure修饰的函数也被称之为纯函数

执行pure函数,不会消耗gas

如果函数中存在以下语句,则被视为读取状态,编译器将抛出警告

  • 读取状态变量
  • 访问address(this).balance或address.balance
  • 访问任何区块、交易、msg等特殊变量(msg.sig与msg.data允许读取)
  • 调用任何未标记为pure的函数
  • 使用包含特点操作码的内联程序集

view类型函数

函数声明为view,表示该函数只能读取状态,不能修改状态

使用view修饰的函数也被称之为视图函数

执行view函数,不会消耗gas

如果函数中存在以下语句,则被认为修改了状态,编译器将抛出异常

  • 修改状态变量
  • 触发事件
  • 创建合约
  • 使用selfdestruct
  • 发送以太(实行了转账)
  • 调用任何不是view函数和pure修饰的函数
  • 使用底层调用
  • 使用包含某些操作码的内联程序集 

payable类型函数 

函数声明为payable,则该函数是一种可以接收以太币的特殊函数,该以太币放入该合约账户

当一个函数被payable修饰,表示调用这个函数,需要附加一些ETH,当然也可以不发(可以采取某些手段强制发)

如果接收的以太币多了,那么它将会保存在合约账户中

温馨提示:没有加payable修饰的函数,你去发送ETH给它,则调用会出错

8.函数的返回值

关键字:

returns

格式: 

returns (出参列表)

通过returns (出参列表)  关键字给函数,即可让函数返回某些值,出参用于写返回值的类型和个数

2种格式

第一种:规定类型方式

function output1 (uint a , uint b) public pure returns (int ,int )

{

  return (a+b , a-b);

第二种:声明变量形式

function output2 (uint a , uint b) public pure returns (uint r )

{

  r = a+b;

}

 

推荐使用第一种

9.接收(保存)函数的返回值

通常定义变量等一些存储单位来存储函数的返回值以便后期使用

例如变量

uint a=0

uint b=0

单一

a=output1(1,2)

b=output2(1,2)

多个

(a,b)=output3(3,5)

(a,b)=output4(3,5)

多个返回值,只接收部分返回值

使用留空的方法即可

( ,b)=output4(3,5)

10.函数重载

重载的定义:

函数名相同,参数不同

参数不同可以是:

  1. 不同类型
  2. 不同个数

温馨提示:函数名相同,参数不同。才是重载的判断依据,而返回值类型不作为重载的判断依据

在外部接口中也存在重载的函数,如果两个外部可见的函数的solidity类型不同,但外部类型相同,则无法重载成功

 

contract Test {
   function getSum(uint a, uint b) public pure returns(uint){      
      return a + b;
   }
   function getSum(uint a, uint b, uint c) public pure returns(uint){      
      return a + b + c;
   }
   function callSumWithTwoArguments() public pure returns(uint){
      return getSum(1,2);
   }
   function callSumWithThreeArguments() public pure returns(uint){
      return getSum(1,2,3);
   }
}

11.Solidity的系统函数-数学函数

Solidity 也提供了内置的数学函数。下面是常用的数学函数:

  • addmod(uint x, uint y, uint k) returns (uint) 计算(x + y) % k,计算中,以任意精度执行加法,且不限于2^256大小。
  • mulmod(uint x, uint y, uint k) returns (uint) 计算(x * y) % k,计算中,以任意精度执行乘法,且不限于2^256大小。

下面的例子说明了数学函数的用法。 

contract Test {   
   function callAddMod() public pure returns(uint){
      return addmod(4, 5, 3);
   }
   function callMulMod() public pure returns(uint){
      return mulmod(4, 5, 3);
   }
} 

运行上述程序:

首先单击callAddMod按钮,然后单击callMulMod按钮查看结果。

结果:

0: uint256: 0
0: uint256: 2

12.Solidity的系统函数-加密函数      

Solidity提供了内置的加密函数,下面常用的加密函数

keccak256(bytes memory) returns(bytes32)   计算输入的Keccak-256散列

使用以太坊的(   Keccak-256   ) 计算HASH值,紧密打包

Keccak256的别名函数sha3(...) returns(bytes32)

sha256(bytes memory) returns(bytes32)   计算输入的sha-256散列

使用 SHA-256计算HASH值,紧密打包

ripemd160(bytes memory) returns(bytes20)  计算输入的RIPEMD-160散列

使用 RIPEMD-160计算HASH值,紧密打包

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) 从椭圆曲线签名中恢复与公钥相关的地址,或在出错时返回零。函数参数对应于签名的ECDSA值: r – 签名的前32字节; s: 签名的第二个32字节; v: 签名的最后一个字节。这个方法返回一个地址。

下面的例子说明了加密keccak256函数的用法。 

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Test {  

   function callKeccak256() public pure returns(bytes32 result){

      return keccak256(abi.encodePacked("ABC"));

      //注意要加abi.encodePacked()

   }  

}

运行上述程序,输出:

  • 0:bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8

温馨提示:可以利用加密函数对字符串进行加密然后判断是否相等

 四、函数进阶--函数修改器(函数修改符)

函数修改器,可以用来轻易改变一个函数的行为,控制函数的逻辑,比如常用于在函数执行前检查某种前置条件 

我们可以将一些通用的操作提取出来,包装为函数修改器,来提高代码的复用性,改善编码效率。

修改器是一种合约属性,可以被继承,同时还可以被派生的合约重写override

但前提是它们被标记为 virtual

修改器的关键字:

 modifier

定义格式:

modifier 函数修改器名 (参数) {

       代码1  //修饰的函数执行前的代码

       _;      //表示被修饰的函数中的代码

       代码2  //修饰的函数执行后的代码

}

使用格式

在函数修饰区写入 修改器函数名()

执行过程:

当一个函数被修改器修饰后,那么先执行修改器里面的代码段,直到遇见_;就开始执行下一个修改器,以此类推,当最后一个修改器执行到了_;就执行函数内的代码。函数内代码执行完再返回去执行剩余的代码。直到修改器内代码全部执行完毕

对于一个函数可以有多个修改器限制,在函数定义的时候依次写上,并加空格分隔,执行的时候也是依次执行。多个修改器是同时限制,也就是说必须满足所有修改器的权限,才可以执行函数体的代码.

1. _ 的作用

函数修改器中有一行代码只有下划线 _ ;  我们认为下划线 _ 代表了被修饰的函数代码。

也就是说,下划线实际上帮我们标记了被 modifier 修饰函数的执行位置。

2. 基本的函数修改器 

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
    uint a = 3;
    modifier requires () {
    // 检查前置条件:判断a是否大于2,如果a大于2,那么执行get函数返回a。
        require(a>2);
        _;
    }
    
    function get() public view requires returns(uint){
        return a;
    }
}

温馨提示:无参的修改器在作为修饰词的时候可以不加() 

3. 带参数的函数修改器

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
    uint public a  = 3;

    modifier requires (uint _x) {
        require( a >_x );//前置检查a是否大于_x
        _;  // 执行get()函数
        a = _x; //修改a的值
    }
    
    function get(uint _x) public requires(_x) returns(uint){
       //修改器接收get函数接收的参数_x
        return a;
    }
}

代码分析

首先给get函数传入参数为2,requires修改器接收传入get函数的参数并传递给修改器函数内部

判断a 是否 大于 _x  ,如果大于则执行get函数,不大于则给报错信息

若执行get函数,则返回值a= 3

接着继续执行修改器函数第三行代码

a = _x            

a的值被修改为_x

查看a的值为2

4.修改器和函数里面的return 

在修改器中或函数内的显式的return语句,仅仅跳出当前的修改器或函数。若被修饰的函数本身具有return 返回值,则该return将再最后执行

// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8.0;
contract infoContract {
    uint a = 10;
    uint c;
    modifier mf(uint _x) {
        a=_x;
        _;
        return;
        c=9;//不执行
    }

    function test(uint _x) public mf(_x)  returns(uint,uint){
        a = 5;
        return(a,c);
        c=2;
    }
}

5.多个修改器同时修饰一个函数

对于一个函数可以有多个修改器限制,在函数定义的时候依次写上,并加空格分隔,执行的时候也是依次执行。多个修改器是同时限制,也就是说必须满足所有修改器的权限,才可以执行函数体的代码

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
    uint a = 10;
    
    modifier mf1 (uint _x) {
        uint c = _x;
        _;
        c = a;
        a = 11;
    }
    
     modifier mf2 () {
        uint c = a;
        _;
    }
    
    modifier mf3() {
        a = 12;
        return ;
        _;
        a = 13;
    }
    
    function test1(uint _x) mf1(_x) mf2 mf3 public  {
        a = 1;
    }
    
     function test2() public view returns (uint)   {
        return a;  
    }  

}

整个执行可看成

uint c = _x

uint c = a

a=12

return

_;

a=13

c=a

a=11

6.复杂示例

我们来看一个函数修改器经典的应用 OpenZeppelin 库中的 Ownable 合约,下面是其中关键的代码:

/// Ownable 可以判断合约的调用者是否为当前合约的owner,
/// 从而避免其他人随意的调用一些合约的关键操作。
/// 同时,owner 可以指定任何其他人为此合约新的 owner,
/// 显然,只有当前owner才能指定其他人为新的owner。
contract Ownable {
    // 变量 owner 指定此合约的owner
    address public owner;
    // 发布事件 - 此合约owner已经换人(此逻辑与modifier无关,可以忽略)
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    // 构造函数 - 创建合约自动执行,初始化合约所有人为合约创建者
    function Ownable() public {
        owner = msg.sender;
    }

    // 定义一个函数修改器
    modifier onlyOwner() {
        // 判断此函数调用者是否为owner
        require(msg.sender == owner);
        _;
    }
    
    // owner可以用此函数将owner所有权转换给其他人,显然次函数只有owner才能调用
    // 函数末尾加上onlyOwner声明,onlyOwner正是上面定义的modifier
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

上述合约的 transferOwnership 函数用于 owner 将所有权转让给其他人,于是在末尾声明 onlyOwner 修改器,onlyOwner 将在 transferOwnership 执行前,先执行

require(msg.sender == owner);

以保证此函数的调用者为 owner ,如果不是 owner 则抛出异常。

五、函数进阶--函数构造器

函数构造器也叫构造函数,是一个特殊函数,它仅能在智能合约部署的时候自动调用一次,之后就不能再次被调用。通常用来完成合约的状态变量初始化工作

如果没有实现构造函数,那合约会添加一个默认的构造函数 construct () { } 啥也没有,什么也没发生

当创建一个合约时,它的构造函数被执行一次,构造函数是可选的(可有可无)。且只允许一个构造函数。这意味着构造函数不能重载

关键字:

constructor

 

定义格式:

constructor (参数列表) {

        构造函数体

}

1.不带参数的构造函数

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
    uint a;
    constructor () {
        a=100;//合约创建后即调用构造方法,a=100
    }
}

//合约创建后即调用构造方法,a=100

给a进行初始化赋值

缺点:

写死了,固定赋值100

2.带参数的构造函数

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
    uint a;
    constructor (uint _a) {
        a=_a;//将构造函数的参数传递给a
    }
}

编辑器部署合约时,如果是有参构造,那么会在合约部署之前给一个框框用于输入构造函数的参数,当合于创建后,参数会给到构造函数,然后执行构造函数

合约创建后即调用构造方法 a=_a;

给a进行初始化赋值

改善了写死,可以在部署前灵活赋值

3.构造函数通常的使用思想

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

contract Test {
   int public a ;
   address public owner;

   constructor(uint _a) public{
     // 将部署者地址存储到owner变量
      owner = msg.sender;
    // 将参数_a存储到a变量
      a = _a;
   }    
}

a初始化了合约部署前的值

owner初始化了 合约创建者的地址

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值