🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 异常(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`验证输入的有效性。这样的分析工具,可以假设正确的输入,减少错误。这样无效的操作码将永远不会出现。