阅读(2921) (14)

函数调用

2022-05-12 11:13:44 更新

内部函数调用

当前合约的函数可以直接(“内部”)调用,也可以递归调用,如以下无意义的示例所示:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;

// This will report a warning
contract C {
    function g(uint a) public pure returns (uint ret) { return a + f(); }
    function f() internal pure returns (uint ret) { return g(7) + f(); }
}

这些函数调用被转换为 EVM 内的简单跳转。这具有不清除当前内存的效果,即将内存引用传递给内部调用的函数非常有效。只能在内部调用同一合约实例的函数。

您仍然应该避免过度递归,因为每个内部函数调用至少使用一个堆栈槽并且只有 1024 个可用槽。

外部函数调用

也可以使用this.g(8);andc.g(2);表示法调用函数,其中 c是合约实例,g是属于 的函数c。通过任何一种方式调用函数g都会导致它被“外部”调用,使用消息调用而不是直接通过跳转。请注意,函数调用this不能在构造函数中使用,因为实际的合约尚未创建。

其他合约的函数必须在外部调用。对于外部调用,所有函数参数都必须复制到内存中。

笔记

从一个合约到另一个合约的函数调用不会创建自己的交易,它是作为整个交易的一部分的消息调用。

在调用其他合约的函数时,您可以通过特殊选项指定调用时发送的 Wei 或 Gas 数量。请注意,不鼓励明确指定 gas 值,因为操作码的 gas 成本可能会在未来发生变化。您发送给合约的任何 Wei 都会添加到该合约的总余额中:{value: 10, gas: 10000}

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.9.0;

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

contract Consumer {
    InfoFeed feed;
    function setFeed(InfoFeed addr) public { feed = addr; }
    function callFeed() public { feed.info{value: 10, gas: 800}(); }
}

您需要将修饰符payableinfo函数一起使用,否则该value选项将不可用。

警告

请注意,仅在本地设置 函数调用的发送量和发送量,最后的括号执行实际调用。so 不调用函数并且和设置丢失,只 执行函数调用。feed.info{value: 10, gas: 800}valuegasfeed.info{value: 10, gas: 800}valuegasfeed.info{value: 10, gas: 800}()

由于 EVM 认为对不存在的合约的调用总是成功的,Solidity 使用extcodesize操作码检查即将被调用的合约是否确实存在(它包含代码),如果不存在则引发异常. 如果返回数据将在调用后被解码,则跳过此检查,因此 ABI 解码器将捕获不存在合约的情况。

请注意,在对地址而不是合约实例进行操作的低级调用的情况下,不会执行此检查。

笔记

对预编译合约使用高级调用时要小心 ,因为即使它们执行代码并且可以返回数据,编译器也会根据上述逻辑认为它们不存在。

如果被调用的合约本身抛出异常或耗尽gas,函数调用也会导致异常。

警告

与另一个合约的任何交互都会带来潜在的危险,特别是如果事先不知道合约的源代码。当前的合约将控制权移交给被调用的合约,这可能会做任何事情。即使被调用的合约继承自已知的父合约,也只需要继承的合约有正确的接口即可。然而,合同的执行可能是完全任意的,因此会造成危险。此外,请做好准备,以防它在第一次调用返回之前调用系统的其他合约,甚至返回调用合约。这意味着被调用合约可以通过其函数改变调用合约的状态变量。以某种方式编写函数,例如,

笔记

在 Solidity 0.6.2 之前,指定值和气体的推荐方法是使用f.value(x).gas(g)(). 这在 Solidity 0.6.2 中已被弃用,并且自 Solidity 0.7.0 起不再可行。

命名调用和匿名函数参数

函数调用参数可以按任何顺序按名称给出,如果它们被包含在下面的示例中。参数列表的名称必须与函数声明中的参数列表一致,但可以是任意顺序。{ }

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract C {
    mapping(uint => uint) data;

    function f() public {
        set({value: 2, key: 3});
    }

    function set(uint key, uint value) public {
        data[key] = value;
    }

}

省略的函数参数名称

未使用的参数(尤其是返回参数)的名称可以省略。这些参数仍将存在于堆栈中,但无法访问。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;

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