值类型和引用类型的区别
Solidity变量类型分为两大类——值类型、引用类型
值类型:变量的存储空间存的是变量的数据
引用类型:变量的存储空间存的是变量数据所在的存储空间的地址
注意:值传递和引用传递。值类型的变量,赋值是简单的值传递,即两个变量占有独立的存储区域。引用类型赋值传递的是变量的引用,即两个变量指向同一存储区域*
值类型——布尔 (bool)
bool: 只有两种值true和false(默认false)。
支持的运算符:
- ! 逻辑非
- && 逻辑与
- || 逻辑或
- == 等于
- != 不等于
实例:
bool a = true;
bool b = !a;
// a == b -> false
// a != b -> true
// a || b -> true
// a && b -> false
逻辑与(&&)和逻辑或(||)都遵循短路原则,即如果根据前一个表达式可以得到运算结果,则不会执行后面的表达式
值类型——整型(int/uint)
- int(m):有符号整数
- uint(m):无符号整数
- m关键字取值为8~256步幅是8 ,表示在内存中2进制的位数,控制了整数的取值范围,不写默认为256。
- uint和int分别是uint256和int256的别名。
- m一定要是8的整数倍
操作
比较:<=,<,==,!=,>=,>(结果为bool)
位操作符:&,|,^(按位异或),~(按位取反)
算术运算符:+, - ,一元 - ,一元 +,*,/,%(取余),**(幂),<<(左移),>>(右移)
注意:
- 除零和取余有零引发异常。
- 左移几位和右移几位相当于乘以或者除以2的几次方,如果参数为负数的话会引发异常。
- 在Solidity中不支持八进制。
- 整形的上溢和下溢
值类型——定点数(Fixed Point Numbers)
注意:Solidity 还没有完全支持定长浮点型。可以声明定长浮点型的变量,但不能给它们赋值或把它们赋值给其他变量。
fixed/ufixed 各种大小的有符号和无符号定点小数,ufixedMxN and fixedMxN关键字M代表定点数占用的二进制位数,N代表定点数能表示多少位小数,M必须是8-256之间的,以8为步幅的整数,N必须是0-80之间的整数,ufixed 和fixed 默认为ufixed128x18和fixed128x18
- 比较运算: <=, <, ==, !=, >=, > (结果为bool)
- 算数运算: +, -, 一元-, 一元 +, *, /, % 取余
值类型——定长字节数组
bytes1, … ,bytes32,允许值以步长1递增。byte默认表示bytes1。
操作:
- 比较:<=,<,==,!=,>=,>(评估为bool)
- 位运算符:&,|,^(按位异或),~(按位取反),<<(左移),>>(右移)
- 索引访问:如果x的类型为bytesI,则0 <= k <I的x [k]返回第k个字节。
成员:
.length产生字节数组的固定长度(只读)。
注意:
- 十六进字面量赋值给定长字节数组时,长度必须和定长数组相同
- 字符串复制给定长数组时,长度必须和定长数组相同
值类型——地址类型(address和address payable)
地址类型有两种形式,大致相同:
- address:保存一个20字节的值(以太坊地址的大小)
- address payable:相同address,但附加成员transfer和send。
运算符
<=, <, ==, !=, >= 和 >
address payable地址成员:
- 以wei位单位返回该地址的余额
<address>.balance(uint256)
- 从当前合约地址中给调用函数的地址账户转入amounts数量(以wei为单位)的金额,如果执行失败,将抛出异常,需要支付2300gas的费用,不可以调整。
<address>.transfer(uint256 amount)
范例:
pragma solidity ^0.5.0;
contract AddressExample {
constructor() public{
}
function giveEthersTo(address payable _toAccount,uint _amount) public{
if (address(this).balance >= _amount){
_toAccount.transfer(_amount);
}
}
function getBalance() view public returns(uint){
return address(this).balance;
}
function() payable external {
}
}
-
如果合约地址调用transfer,那么需要合约地址有payable类型的回退函数,并且会随着转账一块执行回退函数中的代码,如果因为回退函数中的代码执行把gas消耗光了,EVM会抛出异常,转账也会被回退。
-
send是低级对等的转账。执行失败,不会抛出异常,会返回false,需要支付2300gas的费用,不可以调整。推荐使用transfer而不使用send
<address>.send(uint256 amount) returns (bool)
- call(), callcode() 和 delegatecall() 函数
<address>.call(...) returns (bool)
<address>.callcode(...) returns (bool)
<address>.calldelegate(...) returns (bool)
为了和非ABI协议的合约进行交互,可以使用call() 函数, 它用来向另一个合约发送原始数据,支持任何类型任意数量的参数,每个参数会按规则(ABI协议)打包成32字节并一一拼接到一起。如下面的例子:
address(nameReg).call(abi.encodeWithSignature("register(string)", "MyName"));
call函数返回一个bool值,以表明执行成功与否。正常结束返回true,异常终止返回false。但无法获取到结果数据。还可以提供.gas()修饰器进行调用:
address(nameReg)