# 異常(Excepions)
有一些情況下,異常是自動拋出來的(見下),你也可以使用`throw`來手動拋出一個異常。拋出異常的效果是當前的執行被終止且被撤銷(值的改變和帳戶余額的變化都會被回退)。異常還會通過Solidity的函數調用向上冒泡(bubbled up)傳遞。(`send`,和底層的函數調用`call`,`delegatecall`,`callcode`是一個例外,當發生異常時,這些函數返回`false`)。
捕捉異常是不可能的(或許因為異常時,需要強制回退的機制)。
在下面的例子中,我們將如展示如何使用`throw`來回退轉帳,以及演示如何檢查`send`的返回值。
```
pragma solidity ^0.4.0;
contract Sharer {
function sendHalf(address addr) payable returns (uint balance) {
if (!addr.send(msg.value / 2))
throw; // also reverts the transfer to Sharer
return this.balance;
}
}
```
當前,Solidity在下述場景中自動產生運行時異常。
1. 如果越界,或是負的序號值訪問數組。
1. 如果訪問一個定長的`bytesN`,序號越界,或是負的序號值。
1. 如果你通過消息調用一個函數,但在調用的過程中,并沒有正確結束(gas不足,沒有匹配到對應的函數,或他自己出現異常)。底層操作如`call`,`send`,`delegatecall`或`callcode`除外,它們不會拋出異常,但它們會通過返回`false`來表示失敗。
1. 如果在使用`new`創建一個新合約時,但合約的初化化由于類似3中的原因沒有正常完成。
1. 被除數為0。
1. 對一個二進制移動一個負的值。
1. 使用枚舉時,將過大值,負值轉為枚舉類型。
1. 使用外部函數調用時,被調用的對象并不包含代碼。
1. 如果你的`public`的函數在沒有`payable`關鍵字時,卻嘗試在接收`ether`(包括構造函數,和回退函數)。
1. 合約通過一個`public`的`getter`函數(public getter funciton)接收`ether`。
1. 調用一個未初始化的內部函數。
1. `.transfer()`執行失敗
1. `assert`返回`false`
當一個用戶通過下述方式觸發一個異常:
1. 調用`throw`。
1. 調用`require`,但參數值為false。
當上述情況發生時,在Solidity會執行一個回退操作(指令`0xfd`)。與之相對的是,如果發生運行時異常,或`assert`失敗時,將執行無效操作(指令`0xfe`)。在上述的情況下,由此促使EVM撤回所有的狀態改變。這樣做的原因是,沒有辦法繼續安全執行了,因為想要發生的事件并未發生。因為我們想保持交易的原子性(一致性),所以撤銷所有操作,讓整個交易沒有任何影響。
通過`assert`判斷內部條件是否達成,`require`驗證輸入的有效性。這樣的分析工具,可以假設正確的輸入,減少錯誤。這樣無效的操作碼將永遠不會出現。
- Solidity語言
- 入門說明
- Solidity智能合約文件結構
- 智能合約源文件的基本要素概覽
- 值類型
- 類型
- 布爾
- 整型
- 地址
- 字節數組
- 小數
- 字符串
- 十六進制字面量
- 枚舉
- 函數
- 引用類型
- 引用類型
- 數據位置
- 數組
- 數據結構
- 雜項
- 映射
- 左值運算符
- 類型間的轉換
- 類型推斷
- 單位
- 貨幣單位
- 時間單位
- 語言內置特性
- 特殊變量及函數
- 數學和加密函數
- 地址相關
- 進階
- 入參和出參
- 控制結構
- 函數調用
- 創建合約實例
- 表達式的執行順序
- 賦值
- 作用范圍和聲明
- 異常
- 內聯匯編
- 合約詳解
- 合約
- 可見性或權限控制
- 訪問函數
- 函數修改器
- 常狀態變量
- 回退函數
- 事件
- 繼承
- 接口
- 其它
- 庫
- 狀態變量的存儲模型
- 內存變量的存局
- 調用數據的布局