Solidity 教程系列13 - 函数调用

Solidity 教程系列第11篇 - 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 之后,假设我们希望返回两个结果:两个给定整数的和及积,可以这样写:

```js
pragma solidity ^0.4.16;

contract Simple {
function arithmetics(uint _a, uint _b)

top Created with Sketch.