值类型
值类型,指该类型的值,无论是用作函数参数,还是用于赋值,其值总是按值传递。
对于值类型的变量而言,状态变量总是存储在 storage 中,而局部变量总是存储在 memory 中。
布尔类型 bool
布尔类型只有两种可能的值(布尔值): true
表示逻辑上的真,false
表示逻辑上的假。布尔值的零值为 false
。
用比较运算符对两个值的比较,如果成立,结果为 true
,否则,结果为 false
。
可用于一个布尔值的运算符包括:!
逻辑非;以用于两个布尔值之间的运算符包括:&&
逻辑与、||
逻辑或、==
相等、!=
不相等。
注意:
Solidity 与其他常见语言一样 ,
&&
逻辑与、||
逻辑或的运算都遵循短路求值规则,即若求值结果已经确定,程序将不会对表达式剩余的部分进行求值,那一部分的代码将不会执行。
整数类型 int
、uint
、intN
、uintN
intN
、uintN
中的 N
是 8 到 256 之间任意 8 的倍数,表示该类型占用的的二进制位数。int
、 uint
分别是 int256
和 uint256
的别称。
可与整数结合的前缀运算符包括:
- 位运算符:
~
按位取反; - 算术运算符:
-
负、+
正。
可用于两个整数之间的运算符包括:
- 比较运算符:
<=
小于等于、<
小于 、==
等于、!=
不等于、>=
大于等于、>
大于; - 位运算符:
&
按位与、|
按位或、^
按位异或; - 算术运算符:
+
加、-
减、*
乘、/
除、%
求模、**
求幂、<<
左移、>>
右移。
注意:
- 除法总是整除,但有一个例外:两个运算元都是字面量或字面表达式,在需要将一个字面量/字面表达式数值转换为非字面量之前,Solidity 会一直保证此字面量/字面表达式的精确。
- 除以零或用零求模会抛出运行时异常。
- 位移是算数位移,若位移运算的右运算元小于零,将会抛出运行时异常。
定点小数类型 fixed
,ufixed
,fixedMxN
,ufixedMxN
注意:
编写本书时,Solidity 仅支持声明定点小数,而声明出的定点小数甚至不能赋值。读者阅读本书时应查阅 Solidity 官方文档以获知此类型是否已被新版本完全支持。
fixedMxN
和 ufixedMxN
中,M
是从 8
到 256
之间,8 的倍数,表示该类型占用的的二进制总位数;N
是从 0
到80
之间任意的整数,表示该类型小数部分所占用的二进制位数。fixed
、ufixedz
分别是 ufixed128x18
及 fixed128x18
的别称。
可与 定点小数的前缀运算符包括:
- 算术运算符:
-
负、+
正。
可用于两个 定点小数之间的运算符包括:
- 比较运算符:
<=
小于等于、<
小于 、==
等于、!=
不等于、>=
大于等于、>
大于; - 算术运算符:
+
加、-
减、*
乘、/
除、%
求模。
有理数与整数的字面量 (Rational and Integer Literals)
- 十进制整数常量由一串范围在
0
到9
之间的数字组成。Solidity 不允许此类数字序列以0
开头,也不八进制字面量。如:42
; - 小数由一个
.
及点的两边至少一边紧挨着的数字确定而成。如:1.1
、1.
、.1
; - Solidity 同样支持科学计数法,尾数部分可以是分数,而指数部分只能是整数。如:
-1.5e-2
; - 在需要将字面量/字面表达式数值转换为非字面量之前,Solidity 会保证这些字面量/字面表达式的精确。只要这些字面量/字面表达式仍然是整数,就可以对它们应用任意的运算符。
地址类型 address
地址是一种长度为 20
字节的数据类型(通常用来存放以太坊中的地址)。
注意:
在 Solidity
0.5.0
之前,所有合约都被视为继承自地址类型。将来0.5.0
发布后将不在这么处理,不过合约依旧可以显式转换为地址。
可用于两个地址之间的运算符包括:
- 比较运算符:
<=
小于等于、<
小于 、==
等于、!=
不等于、>=
大于等于、>
大于;
地址包含的成员
balance
属性和transfer
函数
地址的 balance
属性用于查询一个地址所拥有的以太坊余额,以 wei 为单位。
地址的 transfer
则是从合约向一个地址转账以太坊的主要方法,同样以 wei 为单位。
以下是官方文档中给出的示例:
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
提示:
向一个合约地址转账,总是会一起调用该被转账合约的回退函数 (fallback function) 。若执行该函数消耗光了所有的 gas,以太坊转账将会被回退,调用转账函数的当前合约会抛出异常并终止。
send
函数
地址的 send
函数功能与 transfer
函数类似,但更底层。若 send
执行失败,send
将返回 false
,而不会导致调用该函数的当前合约抛出异常并终止。
注意:
若递归栈深度超过 1024 层,或转账接受者用尽了 gas,
send
函数便会失败。若要保证安全地转移以太坊,一定要始终检查send
函数的返回值,或者使用transfer
函数。此外,还有一种更好的办法:用转账接收者自行提取以太坊的模式设计合约。
call
函数 、callcode
函数与delegatecall
函数
这三个函数是用来手动调用合约函数的函数,十分底层。
目前先跳过这一部分内容,在函数一节会详细讲解这三个函数。
地址字面量 (Address Literals)
所有通过地址校验和测试(由 EIP-55 定义)的以 0x
开头的十六进制字面量被视作地址字面量,其类型为地址类型。
除去前缀,长度在 39
到 41
之间,但未能通过校验和的十六进制常量会被视为有理数常量。为安全起见,编译器会为此给出警告。
字节与定长字节数组 byte
bytesI
bytesI
中的 I
是 1 到 32 之间的任意整数,表示该类型占用的字节数(或者说 I
* 8 二进制位数)。字节(byte
)实际上是 byte1
的别称。
可用于两个定长字节数组之间的运算符包括:
- 比较运算符:
<=
小于等于、<
小于 、==
等于、!=
不等于、>=
大于等于、>
大于; - 位运算符:
&
按位与、|
按位或、^
按位异或;
此外,定长字节数组还支持以下运算符:
- 索引访问 (Index access):如果
x
的类型为bytesI
,满足0 <= k < I
的表达式x[k]
返回该数组第k + 1
个字节的值(只可读不可写)。 - 位移运算符:定长字节数组做左运算元。右运算元需为整数类型,代表要位移的位数,若其小于零,将会抛出运行时异常。返回的结果是左运算元整体位移的结果,其类型与左运算元一致。
定长字节数组包含的成员有:
length
:数组的固定长度,即其固定的字节数。只读。
提示:
本书之后会讲到的变长数组中,用
byte[]
也可以存放多个字节,但这样会浪费很大的空间—其作为调用参数传递时,数组的每一个元素占用 32 个字节,但实际用到的只有 1 字节。对于变长字节数组,应该用特定的类型bytes
。
字符串字面量 (String Literals)
字符串字面量被 '
单引号或 "
双引号所包围。与 C 语言风格字符串不同,Solidity 中的字符串末尾没有 '\0'
终结符。例如, "foo"
只占用 3 字节,而非 4 字节。
字符串常量支持转义字符,如 \n
、\xNN
、\uNNNN
。\xNN
会插入与对应十六进制相应的字符,而 \uNNNN
则会通过所给 Unicode 码位插入相应的 UTF-8 序列。
只要容量足够,字符串常量可以隐式转换为定长字节数组(bytes1
… bytes32
),变长字节数组(bytes
)或字符串(string
)。后两种类型会在引用类型一节中提到。
还应注意:
字符串本质是utf8编码的字节数组,因此判断字符串长度时要先做转换:
bytes(str).length == 0
字符串式的十六进制字面量 (Hexadecimal Literals)
字符串式的十六进制字面量以 hex
为前缀,紧跟着一对双引号及此双引号所围内容,如:hex"89ABCDEF"
。
字符串式的十六进制字面量的长度必须为 2 的倍数,从高到低,每 2 位表示一个字节。其行为与字符串字面量相似,从前往后占用数组空间,这是一组例子:
// a: 0x00000000abcd
// 0xABCD 是被视作整数,以整数类型的规则隐式转换为字符数组
bytes6 a = 0xABCD;
// b: 0xabcd00000000
// hex"ABCD" 以与字符串字面量相似的规则隐式转换为字符数组
bytes6 b = hex"ABCD";
// c: 43981 <=> 0x00000000abcd
// hex"ABCD" 作为整体隐式转换为整型
int48 c = hex"ABCD";
十六进制字面量拥有和字符串常量相同的类型转换限制。
自定义枚举类型 (enum)
枚举是 Solidity 中定义用户自定义类型的一种方式,只能在函数外定义。
枚举值可以与任意的整数类型相互进行显式转换,但是并不能被隐式转换。显式转换时,运行时会检查值的范围,如果不在范围内,将会抛出异常。
枚举至少需要一位有成员。
定义枚举的形式如下(末尾不能有分号):
enum 枚举名 { 枚举项A, 枚举项B, ... }
例如:
enum RGB {
Red,
Green,
Blue
}
枚举的主要使用方法可参见下例:
function test() public pure returns(bool) {
// 声明相应枚举类型的变量,并赋值特定的枚举项
RGB myColor = RGB.Blue;
// 枚举和整数类型间的显式类型转换
RGB myNewColor = RGB((uint8(myColor)+1) % 3);
// 判断变量的值是否是特定的枚举项
if (myNewColor == RGB.Red) {
return false;
}
return true;
}
函数的类型 (Function Types)
函数类型也是一种值类型,相关内容可阅读本书有关函数的部分。