# 异常(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智能合约文件结构
- 智能合约源文件的基本要素概览
- 值类型
- 类型
- 布尔
- 整型
- 地址
- 字节数组
- 小数
- 字符串
- 十六进制字面量
- 枚举
- 函数
- 引用类型
- 引用类型
- 数据位置
- 数组
- 数据结构
- 杂项
- 映射
- 左值运算符
- 类型间的转换
- 类型推断
- 单位
- 货币单位
- 时间单位
- 语言内置特性
- 特殊变量及函数
- 数学和加密函数
- 地址相关
- 进阶
- 入参和出参
- 控制结构
- 函数调用
- 创建合约实例
- 表达式的执行顺序
- 赋值
- 作用范围和声明
- 异常
- 内联汇编
- 合约详解
- 合约
- 可见性或权限控制
- 访问函数
- 函数修改器
- 常状态变量
- 回退函数
- 事件
- 继承
- 接口
- 其它
- 库
- 状态变量的存储模型
- 内存变量的存局
- 调用数据的布局