Solidity 官方文件中文版(三)
函式呼叫(Function Calls)
內部函式呼叫(Internal Function Calls)
在當前的合約中,函式可以直接呼叫(內部呼叫方式),包括也可遞迴呼叫,來看一個簡單的示例:
contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
}
這些函式呼叫在EVM中被翻譯成簡單的跳轉指令。這樣帶來的一個好處是,當前的記憶體不會被回收。所以在一個內部呼叫時傳遞一個記憶體型引用效率將非常高。當然,僅僅是同一個合約的函式之間才可通過內部的方式進行呼叫。
外部函式呼叫(External Function Calls)
表示式this.g(8);
和c.g(2)
(這裡的c
是一個合約例項)是外部呼叫函式的方式。實現上是通過一個訊息呼叫,而不是直接通過EVM的指令跳轉。需要注意的是,在合約的構造器中,不能使用this
呼叫函式,因為當前合約還沒有建立完成。
其它合約的函式必須通過外部的方式呼叫。對於一個外部呼叫,所有函式的引數必須要拷貝到記憶體中。
當呼叫其它合約的函式時,可以通過選項.value()
,和.gas()
來分別指定,要傳送的ether
量(以wei
為單位),和gas
值。
pragma solidity ^0.4.0;
contract InfoFeed {
function info() payable returns (uint ret) {
return msg.value;
}
}
contract Consumer {
function deposit() payable returns (uint){
return msg.value;
}
function left() constant returns (uint){
return this.balance;
}
function callFeed(address addr) returns (uint) {
return InfoFeed(addr).info.value(1).gas(8000)();
}
}
上面的程式碼中,我們首先呼叫deposit()
為Consumer
合約存入一定量的ether
。然後呼叫callFeed()
通過value(1)
的方式,向InfoFeed
合約的info()
函式傳送1ether
。需要注意的是,如果不先充值,由於合約餘額為0,餘額不足會報錯Invalid opcode
1。
InfoFeed.info()
函式,必須使用payable
關鍵字,否則不能通過value()
選項來接收ether
。
程式碼InfoFeed(addr)
進行了一個顯示的型別轉換,聲明瞭我們確定知道給定的地址是InfoFeed
型別。所以這裡並不會執行構造器的初始化。顯示的型別強制轉換,需要極度小心,不要嘗試呼叫一個你不知道型別的合約。
我們也可以使用function setFeed(InfoFeed _feed) { feed = _feed; }
來直接進行賦值。.info.value(1).gas(8000)
只是本地設定傳送的數額和gas值,真正執行呼叫的是其後的括號.info.value(1).gas(8000)()
。
如果被呼叫的合約不存在,或者是不包程式碼的帳戶,或呼叫的合約產生了異常,或者gas不足,均會造成函式呼叫發生異常。
如果被呼叫的合約原始碼並不事前知道,和它們互動會有潛在的風險。當前合約會將自己的控制權交給被呼叫的合約,而對方几乎可以做任何事。即使被呼叫的合約是繼承自一個已知的父合約,但繼承的子合約僅僅被要求正確實現了介面。合約的實現,可以是任意的內容,由此會有風險。另外,準備好處理呼叫你自己系統中的其它合約,可能在第一呼叫結果未返回之前就返回了呼叫的合約。某種程度上意味著,被呼叫的合約可以改變呼叫合約的狀態變數(state variable)
來標記當前的狀態。如,寫一個函式,只有當狀態變數(state variables)
的值有對應的改變時,才呼叫外部函式,這樣你的合約就不會有可重入性漏洞。
命名引數呼叫和匿名函式引數(Named Calls and Anonymous Function Paramters)
函式呼叫的引數,可以通過指定名字的方式呼叫,但可以以任意的順序,使用方式是{}
包含。但引數的型別和數量要與定義一致。
pragma solidity ^0.4.0;
contract C {
function add(uint val1, uint val2) returns (uint) { return val1 + val2; }
function g() returns (uint){
// named arguments
return add({val2: 2, val1: 1});
}
}
省略函式名稱(Omitted Function Parameter Names)
沒有使用的引數名可以省略(一般常見於返回值)。這些名字在棧(stack)上存在,但不可訪問。
pragma solidity ^0.4.0;
contract C {
// omitted name for parameter
function func(uint k, uint) returns(uint) {
return k;
}
}