智能合约语言 Solidity 教程 – 函数调用

Smart Contracts语言 Solidity 教程 – 函数调用

电报联系方式

函数调用及参数

内部调用是一种在智能合约内部执行函数的方式,它不会创建一个新的以太坊虚拟机(EVM)消息调用。相反,它会直接调用当前合约内部的函数,还可以在函数内部递归地再次执行相同的函数。

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

如下面这个例子:

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)内部的简单指令跳转,而不是创建新的消息调用。这种方法的好处在于,当前内存不会被回收。在内部调用中,可以非常高效地传递内存引用。需要注意的是,内部调用只能在同一个智能合约的不同函数之间进行。

外部函数调用(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 变量赋值。这种方式更加明确,通过传递一个已知的 InfoFeed 类型的参数,可以避免不必要的风险。

Notefeed.info.value(10).gas(800)**仅仅是对发送的以太币和gas值进行了设置,真正的调用是后面的括号()。
调用callFeed时,需要预先存入一定量的以太币,不然会因为余额不足报错。

当与不熟悉的合约互动时,存在潜在风险,即使被调用的合约继承自已知的父合约(这种继承只要求正确实现接口,而不关心实际实现内容)。这是因为与这些合约进行交互等于将控制权交给被调用的合约,允许它几乎可以执行任何操作。

此外,被调用的合约还可以更改调用合约的状态变量。因此,在编写函数时,需要非常小心,以避免潜在的可重入性漏洞,这些漏洞可能导致不希望的行为。我们可以查看安全建议来详细了解如何防范这些风险。

函数参数

与其他编程语言类似,Solidity中的函数可以接受参数作为输入,包括函数类型本身可以作为参数传递。不同于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;
}
}

在Solidity中,你可以省略输出参数的名称,也可以使用return语句明确指定函数的输出值。此外,return语句可以用来返回多个值(如下所示)。

如果没有明确为输出参数赋值,它们默认为0。

输入参数和输出参数可以像普通变量一样在函数内的表达式中使用,也可以作为赋值的目标,如下所示:

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

返回多个值

当一个函数需要返回多个输出参数时,Solidity允许你使用元组(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;
}
}

开发联系:DEXDAO

 

 

 

© 版权声明

Related posts

No comments

No comments...