函数类型

函数也是一种类型(值类型),它是函数的调用方式。函数类型可以被赋值吗,作为参数和返回结果。函数可以分为两类:内部函数(Internal)和外部函数(External)。

  • 内部函数Internal(默认)
    只能在当前合约内被调用(在当前的代码块内,包括内部库函数,和继承的函数中)。
  • 外部函数External
    由地址和函数方法签名两部分组成,可作为外部函数调用的参数,或返回值。
    1
    function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]

external调用时,实际是向目标合约发送一个消息调用。消息中的函数定义部分是24字节大小的消息体。其中20字节为地址,4字节为函数签名。

有两个方式访问函数,一种是直接用函数名a(), 一种是this.a(), 前者用于内部函数,后者用于外部函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity ^0.4.0;

contract Test {
function a() public {}

function b() external {}

function c() public {
a();
//b();//编译报错,不允许在合约的内部调用一个外部的函数.
this.b();
}

}

函数可见性分析

函数的可见性,表明了函数的可以被访问的级别。

  • public(默认)
    public的函数既允许以internal的方式调用,也允许external的方式调用。
    public函数由于被外部合约访问,是合约对外接口的一部分。
  • private
    只能在当前函数中被调用。这禁止了其它合约的访问和修改数据。
  • internal
    只能在当前的合约或者继承的子孙合约中,只允许以internal的方式调用。
  • external
    仅能够被外部合约调用。在内部合约访问时,也只能用外部访问方式访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pragma solidity ^0.4.0;

contract Test {

uint[10] arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

//内部类型函数
function a(uint[10] x) public returns(uint) {
return x[9] * 2;
}

//外部类型函数
function b(uint[10] x) external returns(uint) {
return x[9] * 2;
}


function c() public {
a(arr);
}

function d() public {
this.b(arr);
}

}

运行代码。观察分析耗费的gas值。
这里写图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
transact to Test.a pending ... 

execution cost 491 gas

transact to Test.b pending ...

execution cost 317 gas

transact to Test.c pending ...

execution cost 2934 gas

transact to Test.d pending ...

execution cost 4705 gas

a()和b()比较,可以发现:public函数开销更大。
为什么?
当使用public 函数时,Solidity会立即复制数组参数数据到内存, 而external函数则是从calldata读取,而分配内存开销比直接从calldata读取要大的多。
那为什么public函数要复制数组参数数据到内存呢?是因为public函数可能会被内部调用,而内部调用数组的参数是当做指向一块内存的指针。
对于external函数不允许内部调用,它直接从calldata读取数据,省去了复制的过程。

所以,如果确认一个函数仅仅在外部访问,请用external。

c()和d()比较,可以发现:d()的开销要大。
它采用了this.b()的方法去调用当前合约的内部函数,会有一个大开销的CALL调用,并且它传参的方式也比内部传递开销更大。

因此,当需要内部调用的时候,请用public。

类比Java

  • Java:
作用域 当前类 同一package 子孙类 其他package
public
protected x
default x x
private x x x
  • Solidity:
作用域 当前合约 子孙合约 外部合约
public
private x x
internal x
external x (可以this方式访问) x

参考文档

Solidity官方文档-类型