常量&不变量
常量
常量可以声明在合约中,也可以声明在文件级别:
Constants.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
// 文件级别常量
uint constant KG = 1 * 1000;
contract Constants {
// 合约内定义常量
uint constant TON = 1 * 1000 * KG;
function tenTons() public pure returns (uint) {
return 1 * TON;
}
}
常量的值必须是在编译时,就可以确定下来的。
常量并不是变量,因此即使是声明在合约内部,实际上也不会为常量分配存储槽。相反,由于它的值编译时就确定下来且永远不变,因此编译器会把它直接复制到任何一个使用它的地方。
它类似于我们在 C/C++
中使用的 #define KG (1000)
宏定义常量:在每一个使用 KG
的地方都会原地展开,替换为 (1000)
。
因此,常量实际上是内联到编译后的操作码 opcode 中的。
常量目前支持所有 值类型
以及字节数组(包括 string
)。
不变量
不变量与常量相比,不变量不要求其值必须在编译时就可以确定下来,而是要求其值在合约实例化(或部署时)执行构造函数 constructor
之后确定下来。
因此,不变量只能声明在合约内:
Immutable.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
contract Immutable {
uint256 tenTons;
/* 合约内定义不变量 */
// 可以在声明时赋值,相当于在 constructor 开始时就赋值
uint256 immutable KG = 1 * 1000;
uint256 immutable TON;
address immutable owner;
uint256 immutable neverAssigned;
constructor() {
// 可以多次赋值
TON = 100 * KG;
TON = 1000 * KG;
// 使用 immutable 的 owner,后续无法再修改
owner = msg.sender;
tenTons = 10 * TON;
}
}
可以在 constructor
内为不变量赋值,可以多次赋值。甚至也可以一次都不赋值,此时不变量的值就是它的初始零值。
如果在合约内声明不变量的同时为它赋值,那么就相当于在 constructor
执行时首先为这些不变量赋值。
与常量一样的是,不变量虽然声明在合约内,实际上也不会为它分配存储槽。
不变量与常量直接原地展开不同。不变量在编译时,编译器会标识代码中何处引用了该不变量,并为不变量的引用预留 32 字节的空间。当部署合约时,EVM
执行 constructor
函数,执行完毕后不变量的值就确定了下来,接着 EVM
将修改字节码,检查所有引用了不变量地方,将不变量的值填充到该位置。最后修改完成的代码作为合约代码写入到合约地址中去。
不变量只支持 值类型
,而且无论该值类型长度有多少字节,不变量在栈中参与运算时,都会扩展到32字节。