Solidity基本类型转换
1. 隐式转换
如果一个运算符能支持不同类型。编译器会隐式的尝试将一个操作数的类型,转为另一个操作数的类型,赋值同理。
一般来说,值类型间的互相转换只要不丢失信息,语义可通则可转换。下面,我们来看一个整数转换的例子:
1 2 3 4 5 6 7 8 9 10 11
| pragma solidity ^0.4.0;
contract Int{ function conversion() returns (uint16){ uint8 a = 1; //隐式转换 uint16 b = a;
return (b); } }
|
上面的例子中,我们将一个uint8
的变量a
隐式的转换为了uint16
。同理它还支持转为uint32
,uint128
和uint256
。
另外,无符号整数可以被转为同样,或更大的字节的类型。但需要注意的是,不能反过来转换。由于address
是20字节大小,所以它与int160
大小是一样。
1 2 3 4 5 6 7 8 9
| pragma solidity ^0.4.0;
contract IntToAddress{ function f() returns (uint){ uint160 i = 10; address addr = i; return addr.balance; } }
|
上面的例子中,将uint160
的i
转为了一个address
。
2. 显式转换
编译器不会将语法上不可转换的类型进行隐式转换,此时我们要通过显式转换的方式,比如将一个有符号整数,转为一个无符号整数。
1 2 3 4 5 6 7 8 9 10 11
| pragma solidity ^0.4.0;
contract ExplicitConversion{ function f() returns (int8){ uint8 a = 1;
//强制转换 int8 b = int8(a); return b; } }
|
3. 类型推断
有时为了方便,我们不会显式定义类型。但由于编译器,会自动挑选一个最恰当的类型,所以会常常留下坑,我们来看这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pragma solidity ^0.4.4;
contract Test{ function a() returns (uint){ uint count = 0; for (var i = 0; i < 2000; i++) { count++; if(count >= 2100){ break; } } return count; } }
|
大家可以想想上述代码运行的结果。
上述代码运行的结果实际为2100
。原因是因为var i = 0
定义时,通过类型推断,i
的实际类型为uint8
,所以它会一直循环,如果没有count >= 2100
这个判断语句,这个循环将永远不会结束。
4. 一些常见的转换方案
uint转为bytes
将一个uint转转bytes,可以使用assembly
1。
1 2 3 4
| function toBytes(uint256 x) returns (bytes b) { b = new bytes(32); assembly { mstore(add(b, 32), x) } }
|
上面的转换方式可能是效率最高的方式。
string转为bytes
string
可以显示的转为bytes
。但如果要转为bytes32
,可能只能使用assembly
。
1 2 3 4 5 6 7 8 9 10 11 12 13
| pragma solidity ^0.4.0;
contract StringToBytes{ function StringToBytesVer1(string memory source) returns (bytes result) { return bytes(source); }
function stringToBytesVer2(string memory source) returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } }
|
address转为string
address类型在solidity中不能像Go语言类似的string(address)
的转换,如果使用abi.encodePacked(x)
进行转换,会的到一串我们难以解读的字符串,我们无法读取通过ABI编码的字符串,因此要对ascii字符进行转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function toAsciiString(address x) internal view returns (string memory) { bytes memory s = new bytes(40); for (uint i = 0; i < 20; i++) { bytes1 b = bytes1(uint8(uint(uint160(x)) / (2**(8*(19 - i))))); bytes1 hi = bytes1(uint8(b) / 16); bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); s[2*i] = char(hi); s[2*i+1] = char(lo); } return string(s); }
function char(bytes1 b) internal view returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); else return bytes1(uint8(b) + 0x57); }
|