智能合约语言 Solidity 教程系列13 - 函数调用

  • Tiny熊
  • 更新于 2018-08-09 11:17
  • 阅读 14067

这是Solidity教程系列文章第13篇介绍函数调用, 本文会介绍函数使用元组返回多个值,通过命名方式进行参数调用以及如何省略函数参数名称。

Solidity 系列完整的文章列表请查看分类-Solidity

这是Solidity教程系列文章第13篇介绍函数调用, 本文会介绍函数使用元组返回多个值,通过命名方式进行参数调用以及如何省略函数参数名称。

Solidity 系列完整的文章列表请查看分类-Solidity

写在前面

Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解, 如果你还不了解,建议你先看以太坊是什么

欢迎订阅区块链技术专栏阅读更全面的分析文章。

函数调用及参数

函数类型一节中,我们介绍过Solidity 中有两种函数调用方式:内部函数调用和外部函数调用,这一节我们进一步介绍。

内部函数调用(Internal Function Calls)

内部调用,不会创建一个EVM消息调用。而是直接调用当前合约的函数,也可以递归调用。 如下面这个的例子:

pragma solidity ^0.4.16;

contract C {
    function g(uint a) public pure returns (uint ret) {
      return f();       // 直接调用
    }

    function f() internal pure returns (uint ret) {
     return g(7) + f();    // 直接调用及递归调用
    }
}

这些函数调用被转换为EVM内部的简单指令跳转(jumps)。 这样带来的一个好处是,当前的内存不会被回收。在一个内部调用时传递一个内存型引用效率将非常高的。当然,仅仅是同一个合约的函数之间才可通过内部的方式进行调用。

外部函数调用(External Function Calls)

外部调用,会创建EVM消息调用。 表达式this.g(8);和c.g(2)(这里的c是一个合约实例)是外部调用函数的方式,它会发起一个消息调用,而不是EVM的指令跳转。需要注意的是,在合约的构造器中,不能使用this调用函数,因为当前合约还没有创建完成。

其它合约的函数必须通过外部的方式调用。对于一个外部调用,所有函数的参数必须要拷贝到内存中。

当调用其它合约的函数时,可以通过选项.value(),和.gas()来分别指定要发送的以太币(以wei为单位)和gas值,如:

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() public payable returns (uint ret) { return 42; }
}

contract Consumer {
    InfoFeed feed;

    function setFeed(address addr) public {
      feed = InfoFeed(addr);
    }

    function callFeed() public {
      feed.info.value(10).gas(800)();  // 附加以太币及gas来调用info
    }
}

info()函数,必须使用payable关键字,否则不能通过value()来接收以太币。

表达式InfoFeed(addr)进行了一个显示的类型转换,表示给定的地址是合约InfoFeed类型,这里并不会执行构造器的初始化。 在进行显式的类型强制转换时需要非常小心,不要调用一个我们不知道类型的合约函数。

我们也可以使用function setFeed(InfoFeed _feed) { feed = _feed; }来直接进行赋值。 注意feed.info.value(10).gas(800)仅仅是对发送的以太币和gas值进行了设置,真正的调用是后面的括号()。 调用callFeed时,需要预先存入一定量的以太币,要不能会因余额不足报错。

如果我们不知道被调用的合约源代码,和它们交互会有潜在的风险,即便被调用的合约继承自一个已知的父合约(继承仅仅要求正确实现接口,而不关注实现的内容)。 因为和他们交互,相当于把自己控制权交给被调用的合约,对方几乎可以利用它做任何事。 此外, 被调用的合约可以改变调用合约的状态变量,在编写函数时需要注意可重入性漏洞问题(可查看安全建议)。

函数参数

与其他语言一样,函数可以提供参数作为输入(函数类型本身也可以作为参数); 与Javascript和C不同的是,solidity还可以返回任意数量的参数作为输出。

输入参数

输入参数的声明方式与变量相同, 未使用的参数可以省略变量名称。假设我们希望合约接受一种带有两个整数参数的外部调用,可以这样写:

pragma solidity ^0.4.16;

contract Simple {
    function taker(uint _a, uint _b) public pure {
        // 使用 _a  _b
    }
}

输出参数

输出参数的声明和输入参数一样,只不过它接在returns 之后,假设我们希望返回两个结果:两个给定整数的和及积,可以这样写:

pragma solidity ^0.4.16;

contract Simple {
    function arithmetics(uint _a, uint _b)
        public
        pure
        returns (uint o_sum, uint o_product)
    {
        o_sum = _a + _b;
        o_product = _a * _b;
    }
}

可以省略输出参数的名称,也可以使用return语句指定输出值,return可以返回多个值(见下文)。 返回一个没有赋值的参数,则默认为0。

输入参数和输出参数可以在函数内表达式中使用,也可以作为被赋值的对象, 如:

contract Simple {
    function taker(uint _a, uint _b) public pure returns (uint _c) {
        _a = 1;
        _b = 2;
        _c = 3;
    }
}

返回多个值

当一个函数有多个输出参数时, 可以使用元组(tuple)来返回多个值。元组(tuple)是一个数量固定,类型可以不同的元素组成的一个列表(用小括号表示),使用return (v0, v1, ..., vn) 语句,就可以返回多个值,返回值的数量需要和输出参数声明的数量一致。

    function f() public pure returns (uint, bool, uint) {
        // 使用元组返回多个值
        return (7, true, 2);
    }

    function callf() public {
        uint x;
        bool y;
        uint z;
        // 使用元组给多个变量赋值
        (x, y , z)  = f();
    }

补充关于元组的介绍

上面的代码中,使用了元组返回多个值及使用元组给多个变量赋值,给多个变量赋值通常也称为解构(解构的概念在函数式语言中较为常见),再来看看元组的一些用法,比如元组可以交换变量值,如:

(x, y) = (y, x);

元组支持省略一些元素, 如:

(x, y, ) = (1, 2, 4);

开头的元素也可以省略,如:

(, y, ) = (1, 2, 4);

注意 (1,) 是一个一个元素的元组, (1) 只是1。

使用命名参数调用

函数调用的参数,可以通过指定名称的方式调用,使用花括号{} 包起来,参数顺序任意,但参数的类型和数量要与定义一致。 如:

pragma solidity ^0.4.0;

contract C {
    function f(uint key, uint value) public {
        // ...
    }

    function g() public {
        f({value: 2, key: 3});  // 命名参数
    }
}

省略函数参数名称

没有使用的参数名称可以省略(一般常见于返回值)。这些参数依然在栈(stack)上存在,但不可访问。

pragma solidity ^0.4.16;

contract C {
    // omitted name for parameter
    function func(uint k, uint) public pure returns(uint) {
        return k;
    }
}

强烈安利一门课程给大家:深入详解以太坊智能合约语言Solidity


学习中如遇问题,欢迎到区块链技术问答提问,这里有专家为你解惑。 深入浅出区块链 - 高质量的区块链技术博客+问答社区,为区块链学习双重助力

点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
Tiny熊
Tiny熊
0x1231...6564
登链社区发起人 登链团队对 DEFI 应用有深刻的理解和丰富的开发经验,如果你有开发、审计、培训合作等需求, 加我微信:xlbxiong 。 咨询问题在问答区提问即可,微信好友太多,不看问题,请凉解~