0.8.0版本开始,算术运算有两个计算模式,一个是“wrapping”(截断)模式或为“unchecked”(不检查)模式,即在发生溢出的情况下会进行“截断”,不会触发失败异常,从而得靠引入额外的检查库来解决这个问题(如OpenZeppelin中的SafeMath库);另一个是"checked"(检查)模式。默认情况下,算术运算使用的是“checked”模式,会进行溢出检查,如果结果溢出,会出现失败异常回退。
举个栗子:
uint8最大值为255,255加上一个大于0的整数会溢出(上溢),加上unchecked{}则不检查是否溢出,溢出则截断显示;否则默认使用截断模式检查溢出,发生异常回退。
测试0.8.0或以上版本
测试代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
/// @dev 示例合约:uint8最大值为255,255加上一个大于0的整数会溢出,加上unchecked不会检查是否溢出
contract TestUncheck {
/// @dev 检查溢出(checked模式)
function withoutUnchecked() external pure returns (uint8) {
uint8 a = 255;
uint8 b = 5;
return (a + b);
}
/// @dev 不检查溢出(unchecked模式或称为截断模式)
function withUnchecked() external pure returns (uint8) {
uint8 a = 255;
uint8 b = 5;
// 不检查溢出,输出溢出结果
unchecked {
return (a + b);
}
}
}
测试检查模式(checked)
检查模式,由于数据溢出,发生异常回退。
测试截断模式 (unchecked或wrapping)
截断模式,虽然255+5溢出了,但输出了溢出结果4。
unchecked
代码块可以在代码块中的任何位置使用,但不可以替代整个函数代码块,同样不可以嵌套。此设置仅影响语法上位于
unchecked
块内的语句。 在块中调用的函数不会此影响。
测试0.8.0之前版本
0.8.0之前版本为“wrapping”(截断)模式,即在发生溢出的情况下会进行“截断”,不会触发失败异常,从而得靠引入额外的检查库来解决这个问题(如OpenZeppelin中的SafeMath库)
测试代码
测试一个离0.8.0最近版本0.7.6,合约代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
/// @dev 测试0.8.0以前版本,溢出为截断模式
contract TestOverflow {
/// @dev 不检查溢出(wrapping模式)
function overflow() external pure returns (uint8) {
uint8 a = 255;
uint8 b = 5;
return (a + b);
}
}
测试结果(wrapping模式)
输出截断数据,即为不检查模式