# 函數調用(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()`函數發送1`ether`。需要注意的是,如果不先充值,由于合約余額為0,余額不足會報錯`Invalid opcode`<sup id="fnref1">[1](#fn1)</sup>。
`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;
}
}
```
#### 參考資料
---
<li id="fn1">
如何使用`Remix`向合約發送ether參見這里。[http://me.tryblockchain.org/%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.html](http://me.tryblockchain.org/%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.html)?[?](#fnref1)
</li>
- Solidity語言
- 入門說明
- Solidity智能合約文件結構
- 智能合約源文件的基本要素概覽
- 值類型
- 類型
- 布爾
- 整型
- 地址
- 字節數組
- 小數
- 字符串
- 十六進制字面量
- 枚舉
- 函數
- 引用類型
- 引用類型
- 數據位置
- 數組
- 數據結構
- 雜項
- 映射
- 左值運算符
- 類型間的轉換
- 類型推斷
- 單位
- 貨幣單位
- 時間單位
- 語言內置特性
- 特殊變量及函數
- 數學和加密函數
- 地址相關
- 進階
- 入參和出參
- 控制結構
- 函數調用
- 創建合約實例
- 表達式的執行順序
- 賦值
- 作用范圍和聲明
- 異常
- 內聯匯編
- 合約詳解
- 合約
- 可見性或權限控制
- 訪問函數
- 函數修改器
- 常狀態變量
- 回退函數
- 事件
- 繼承
- 接口
- 其它
- 庫
- 狀態變量的存儲模型
- 內存變量的存局
- 調用數據的布局