以太坊智能合约语言Solidity - 5 函数

Posted shiyivei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了以太坊智能合约语言Solidity - 5 函数相关的知识,希望对你有一定的参考价值。

5. 函数

5.1 函数重载

函数不能重名

函数的不同不以返回值类型不同为依据,但是参数类型或参数数量不一样,则可以被认为是不同的函数,可以被重载。另外int160和address是同一种类型,不能重载

但是重名函数怎么调用呢?编译器能自动判断就会自动判断,不能判断则会报错

5.2 函数命名参数

pragma solidity ^0.4.0;

contract funcParam

    uint public num;
    string public name;

    //通过输入函数参数为状态变量赋值
    function setParam(uint _num,string _name) 
        num = _num;
        name = _name;
    
    //调用赋值函数
    function Test()
        setParam(_num:100, _name:"xiaoyu");
    
    //调用赋值函数
    //因为参数是显式指定的,所以顺序并不会影响赋值
    function Test2()
        setParam(_name:"xiaoxia", _num:200);
    
    //不显式指定
    function Test4()
        setParam(10,"zhengjianxun");
    

5.3 返回值特性

返回值的几种规范写法如下

pragma solidity ^0.4.0;

contract funcReturn

    // 返回值可以指定名称
    // 10
    function returnTest() returns(uint mul) 
        uint a = 10;
        return a;
    

    // 100
    function returnTest2() returns(uint mul) 
        mul = 100;
    

    //会优先返回 return后面的值
    // 100
    function returnTest3() returns(uint mul) 
        uint a = 10;
        mul = 100;
        return a;
    

    // 1
    function returnTest4() returns(uint mul) 
        uint a = 10;
        return 1;
    

    //可以返回多个值
    // 6,8
    function returnTest5(uint a,uint b)view returns(uint add,uint mul) 
        add = a+b;
        mul = a*b;
    

    //如下这种写法也是可以的,其实返回值的名称和return中的名称无关,因此我们甚至可以实现交换
    // 6,8
    function returnTest6(uint a,uint b)view returns(uint add,uint mul) 
        return(a+b,a*b);
    

5.4 变量的生命周期与作用域

状态变量可以和函数内部同名的变量同时存在,修改函数内部变量并不会影响状态变量。另外,一旦变量作为参数名称,函数内部不能再定义同名变量

5.5 external 权限修饰符

pragma solidity ^0.4.0;

contract father

    //internal dahan不能被外部调用,也就是说编译后不会显示,把internal改为public就可以了,但是如果改为external,就只能在外部调用了,但是可以用this.方式调用它
    function dahan() external pure returns(string)
        return "dahan";
    
   
    //可以在合约内部调用,使用this
    function dahanTest() public view 
        this.dahan();
    


//可以在合约的内部再创建一个合约,然后调用
contract externalTest

    father f = new father();

    function externalTestIt() returns(string)
        f.dahan();
        
    
        


//也可以在继承合约中被调用
contract son is father
    function test() public view returns(string)
    return dahan();
    

5.6 值传递与副本拷贝

我们来看一看变量的值传递(本质上是值的拷贝)案例

pragma solidity ^0.4.0;

contract passAndCopyValue
    
    uint public a = 100;
    //将a的值拷贝给b,改变b并不影响a的值
    uint public b = a;

    function changeIt() 
        b = 999;
    
    // 999,100
    function changeIt2()view returns(uint,uint) 
        uint a1 = 100;
        uint b1 = a1;

        //更改a1后也不影响b1的值,因为是拷贝
        a1 = 999;
        return (a1,b1);
    
    //input 1000, output 1001
    function changeIt3(uint num)returns(uint) 
        num ++;

        return(num);
    

    //101
    function test()view returns(uint)
        return changeIt3(a);
    

5.7 constant关键字

函数内部的constant关键字和view是等价的

全局变量、constant属性则说明变量不支持更改,等同于其它语言中常量

pragma solidity ^0.4.0;

contract constantTest

    uint public kk = 100;
    uint public constant num = 50;

    // 等同于view
    function constTest() public constant returns(uint)
        return kk;
    

    function changeIt()
        //局部变量无constant属性,全局变量一旦被加上了constant属性,就无法在被修改
        //uint public constant num = 50;
    

    // 当前版本solidity只有int系列/string/固定长度的bytes数组可以加上constant属性

5.8 构造函数

pragma solidity ^0.4.0;

contract constructor

    uint public a;

    //构造函数与合约名同名,并且在一开始就会被调用
    //用来初始化变量
    //构造函数智能有一个
    // function constructor()
    //     a = 100;
    // 

    //构造函数可以有参数
    //input 999,0,output 999
    function constructor(uint _a, uint _b)
        a = _a;
    

//在一个文件中再建一个合约
contract constructor2
    
    uint public a;
    address owner;

    //使用关键字创建构造函数
    //同样下列构造函数可以增加参数
    //构造函数可获取合约的拥有者
    //10
    constructor() 
        a=10;
        owner = msg.sender;
    

5.9 modifier的功能

pragma solidity ^0.4.0;

contract modifierTest

    //定义两个状态变量
    address public owner;
    uint public num = 0;

    //使用构造函数初始化变量
    constructor() 
        owner = msg.sender;
    
    //判断
    //在这里modifier就是用来提供判断功能的
    modifier OnlyOwner
        require(msg.sender == owner);
        _;
    
    //若判断成功则可以执行如下函数
    function changeIt(uint _num) OnlyOwner
        num = _num;
    

我们再来看一个之前的案例

pragma solidity ^0.4.0;

contract mappingTest

    //定义两个映射
    mapping(address => uint) idmapping;
    mapping(uint => string)  namemapping;

    uint public sum = 0;

    // 输入"zhengjianxun" 
    // 如下可以使用同一个地址重复注册,所以我们使用modifier做个判断,如果这个地址有对应的编号,就会报错

    modifier control
        require(idmapping[msg.sender]==0);
        _;
    
    function register(string name) control
        address account = msg.sender;
        sum ++;
        //往映射里填充数据
        idmapping[account] = sum;
        namemapping[sum] =name;
    

    // 输入0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 : 1
    function getIdByAddress(address are) view returns(uint)
        return idmapping[are];
    

    // 输入1: zhengjianxun
    function getNameById(uint id) view returns(string)
        return namemapping[id];
    

实现代码重用性和扩展性

pragma solidity ^0.4.0;

contract modifierTest
    uint public level = 100;

    string public name;

    uint public DNA;

    //通过mondifier判断体就不用重复在每个函数中编写require了,这可以增加代码的重用性和扩展性
    modifier controlevel(uint needLevel) 
        require(level >= needLevel);
        _;
    

    //“张建勋”
    function changeName() controlevel(2)
        //require(level >=2);

        name = "zhengjianxun";
    
    //999
    function changeDNA() controlevel(10)
        //require(level >=10);

        DNA = 999;
    

多个modifier的执行顺序

pragma solidity ^0.4.0;

contract modifire
    uint public a =0;

    modifier mod1 
        a=1;
        _;
        a=2;
    

    modifier mod2
        a =3;

        _;

        a =4;
    
    //此时执行顺序是:执行mod1中的a=1,接着执行mod2中的所有内容,最后再继续执行mod1中剩余的内容
    //output:2
    function test() mod1 mod2
        a =100;
    

5.10 面向对象的连续继承

合约可以连续继承:祖父-父亲-儿子

合约内部的变量和函数可以被继承合约直接或者间接的获取和调用

5.11 遗传特性:继承中的权限

对于变量而言:父合约中的public、internal状态变量都可以被子合约继承,private和external状态变量不可以被继承

对于函数而言:父合约中的public、internal、external都可以被继承,external需要通过this.来访问,private函数不可被继承

5.12 全局变量自动getter函数

pragma solidity ^0.4.0;

contract getter 
        //状态变量前面加public相当于写了一个返回该变量的函数
        uint public num = 100;

         mapping(uint => string) public map;
        //等价于生成function map(uint key) external returns(string)...

        //但是生成的get函数默认是external的,因此智能通过this.的方式调用
        function test()
            this.num();
        

        //赋值
        function test2()
            map[2] = "zhengjianxun";
        
        //赋值后通过this.调用
        //zhengjianxun
        function test3() returns(string)
            return this.map(2);
        
    

map的多层映射

pragma solidity ^0.4.0;

contract getter
    //map的嵌套映射

    mapping(uint => mapping(uint=>mapping(uint=>string))) public map;

    //0,1,2
    function test()
        map[0][1][2]="zhengjianxun";
    

5.13 继承中的重载

对于属性:当子合约和父合约有相同的属性时,前者会覆盖后者的内容

对于函数:子合约中的函数会覆盖父合约中的函数

多重继承:

5.14 合约的销毁

pragma solidity ^0.4.0;

contract destruct
    uint public money = 0;

    //通过一个构造函数获取合约拥有者
    constructor()
        onwer = msg.sender;
    

    function increment()
        money +=10;
    
    //如果是合约拥有者,则可以销毁合约
    function kill()
        if (msg.sender == owner)
        selfdestruct(owner);
        
    

第一行代码:以太坊-使用Solidity语言开发和测试智能合约

智能合约是以太坊的核心之一,用户可以利用智能合约实现更灵活的代币以及其他DApp。不过在深入讲解如何开发智能合约之前,需要先介绍一下以太坊中用于开发智能合约的Solidity语言,以及相关的开发和测试环境。

智能合约就是运行在以太坊上的程序。客户端可以通过Web3.js API调用智能合约,而智能合约本身又可以直接访问以太坊网络,也就是说,智能合约前面连接着客户端,后面连接着以太坊网络,起到了承前启后的作用,而且通过智能合约,可以让整个以太坊网络更灵活,可控性更强。其实智能合约的作用相当于微软Office中的VBA,一个功能强大的领域脚本语言。智能合约的开发语言是Solidity,那么Solidity是什么呢?应该如何在以太坊网络上运行用Solidity语言编写的智能合约呢?本文将会揭晓这些问题的答案。

1. 什么是Solidity语言

Solidity是一种用于编写智能合约的高级语言,运行在Ethereum虚拟机(以太坊虚拟机,EVM)之上。那么Solidity到底是怎样一种编程语言呢?或者说Solidity语言的主要特性是什么呢?请继续往下看。

Solidity语言的语法接近于JavaScript,是一种面向对象的语言。但作为一种真正意义上运行在网络上的去中心智能合约,它又有很多的不同,下面列举一些Solidity语言的主要特性。

  • 以太坊底层是基于帐户的,因此在Solidity语言中有一个特殊的Address数据类型。用于定位用户,定位合约,定位合约的代码(合约本身也是一个帐户)。
  • 由于Solidity语言内嵌框架是支持支付的,所以提供了一些关键字,如payable,可以在语言层面直接支持支付。
  • Solidity语言可以将数据存储在区块链上,数据的每一个状态都可以永久存储,所以需要确定变量使用的是内存,还是区块。
  • 运行环境是在去中心化的网络上,会比较强调合约或函数执行的调用的方式。因为原来一个简单的函数调用变为了一个网络上的节点中的代码执行。
  • 最后一个非常大的不同则是Solidity语言的异常机制,一旦出现异常,所有的执行都将会被回撤,这主要是为了保证智能合约执行的原子性,以避免中间状态出现的数据不一致。有点类似于数据库中的事务回滚。

2. 用Solidity语言开发智能合约

Solidity是一种图灵完备的编程语言,所以编程的方式与Java、C++类似。不过Solidity语言中并没有类的概念,但有一个合约的概念,用关键字contract表示。任何一个Solidity程序,都必须至少有一个合约(contract)。在合约中可以编写Solidity函数,类似于类中的方法。Solidity源代码文件的扩展名是sol,下面的例子给出了一个简单的使用Solidity语言编写的智能合约的例子,以便读者对Solidity语言和智能合约有一个感性的认识。

下面的例子给出了一个名为Calc的智能合约程序,在该智能合约中有一个add函数,用于将两个无符号整数相加,并返回相加的结果。

pragma solidity ^0.4.0;
contract Calc{
    function add(uint a,uint b) returns (uint){
        return a + b;
    }
}

尽管现在还没有正式讲解Solidity语言和智能合约,不过从这段简单的智能合约代码也可以了解Solidity语言的结构。首先,智能合约的第1行需要使用pragma solidity指定Solidity编译器的最低版本,本例是0.4.0,也就是说,要编译这段Solidity程序,Solidity编译器的版本不能低于0.4.0。要记住,在版本号前面要加上“^”。

接下来就是用contract关键字声明智能合约,语法与类非常接近,智能合约的名字跟在contract关键字后面,智能合约中的代码用一对花括号括起来。

最后是在智能合约中声明若干个函数,函数的语法与JavaScript类似(都是使用function关键字声明函数),不过也不完全相同,因为Solidity是强类型的编程语言,而JavaScript是弱类型的编程语言。也就是说,声明Solidity变量需要指定数据类型,如本例的uint,表示无符号整数类型。函数的返回值类型需要在函数声明的结尾通过returns关键字指定。如本例的returns(uint),函数返回值与C风格的编程语言相同,仍然使用return语句指定函数返回值。Solidity语言的每一条语句后面都要跟分号(;)。

3. 使用Remix运行智能合约

学习编写智能合约最重要的一步就是运行智能合约,否则无法知道我们编写的智能合约程序是否正确。在正常情况下,应该将智能合约部署在以太坊网络上,然后通过以太坊客户端调用,不过现在还没有讲如何将智能合约部署到以太坊网络上,以及如何调用智能合约。所以目前只能使用最简单的方式测试智能合约。以太坊官方提供了一个在线的智能合约编写和测试环境:Remix,通过这个工具,可以用不同的方式测试智能合约。
在浏览器地址栏输入如下的Url后,会进入Remix页面。

https://remix.ethereum.org

Remix页面主要包含如下4部分。

  • 智能合约列表区域,位于Remix页面的左侧,如果第一次使用Remix,这个区域只有browser和config两个节点,如果以前使用Remix创建过智能合约,会在browser节点下方显示曾经创建过的智能合约文件(.sol文件)。
  • 代码区域,位于Remix页面的中上部,用于编写智能合约代码。
  • 日志区域,位于Remix页面的中下部,运行智能合约后,会将日志信息输出到这一区域。
  • 设置区域,位于Remix页面右侧,在这一区域可进行各种设置,如将智能合约部署在以太坊网络上,运行智能合约等。

除了这4部分外,在Remix页面左上角还有一排按钮,其中最左侧的加号按钮用于新建智能合约,最右侧的加号和减号按钮分别用于增加和减少智能合约代码的字号。Remix页面的整体布局如下图所示。

技术分享图片

接下来单击Remix页面左上角的加号按钮,会弹出一个如下图所示的页面,在“File Name”文本框输入“Calc.sol”,然后单击“OK”按钮创建新的智能合约。

技术分享图片

将上一节给出的智能合约代码输入代码区域,可以点击加号和减号按钮将代码字体调整到自己感觉舒服的程度,效果如下图所示。在设置区域会出现一些警告,并不需要管它们。

技术分享图片

在设置区域切换到“Run”页面,所有的设置保持默认值即可,然后点击中间的“Deploy”部署Calc合约。成功部署Calc合约后,会在“Run”页面下方根据Calc合约中的函数显示相应的按钮,如本例中只有一个add函数,并且该函数有两个参数,所以在“Run”页面下方会出现一个“add”按钮,在按钮旁边的文本框输入“3,4”,表示add函数的两个参数值,如下图所示。

技术分享图片

最后单击“add”按钮执行add函数,会在日志区域显示相应的信息,然后单击日志区域输出信息的向下箭头,会在日志区域显示一个表格,在“decoded output”行会显示add函数的返回值(计算结果),如下图所示。

技术分享图片

通过本节的若干步骤,终于成功运行了Calc智能合约的add函数,并获得了add函数的返回值(本例是7),不过这个智能合约程序并没有部署在以太坊网络上,而是在本地运行的,也就是说,本节其实是通过模拟的方式运行了本地合约,这种运行方式只能测试智能合约中的函数的逻辑是否正确,并不能将以太坊客户端、以太坊网络和智能合约放到一起联调,所以在实际的场景中,需要将智能合约部署到以太坊网络上才能完整地对其进行测试。

《第一行代码:以太坊》开始转载了

以上是关于以太坊智能合约语言Solidity - 5 函数的主要内容,如果未能解决你的问题,请参考以下文章

智能合约语言 Solidity 教程系列5 - 数组介绍

智能合约语言 Solidity 教程系列10 - 完全理解函数修改器

第一行代码:以太坊-使用Solidity语言开发和测试智能合约

智能合约语言 Solidity 教程系列5 - 数组介绍

智能合约语言 Solidity 教程系列8 - Solidity API

智能合约语言 Solidity 教程系列8 - Solidity API