字面量 值类型
字面量即在代码中直接给出的值,例如代码中的 0x01
, "Hello, World!"
等都属于字面量,甚至可以包含运算:1 + 2
。
代码中给出的字面量经过运算、处理后保存在字面量类型中。
数字字面量
数字字面量可以由以下方式给出:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeNumberLiterals {
function num() public pure returns (int, int, int) {
return (
0x01, // 16 进制
3.14e10, // 科学计数法
123_456 // 下划线分隔符
);
}
}
需要注意,Solidity
不支持以 0 开头的八进制写法。下划线分隔符是为了方便阅读,本身没有意义,会被 Solidity
忽略。
只包含数字字面量和运算符的表达式,也是一个字面量:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeNumberLiteralsCompute {
function num() public pure returns (int, int, int) {
return (
1.5 * 2, // 等于 3,为整数
(3 / 2) * 2, // 3 / 2 不会取整为 1,而是 1.5,结果为 3,整数
(2 ** 256 + 1) - 2 ** 256 // 不会溢出,结果仍为 1
);
}
}
对于只包含数字字面量的运算表达式,其值是可以提前确定的,Solidity
计算出结果后,将其保存为一个单一的字面量类型。由于字面量类型此时并非 int
型,上例中的 (2 ** 256 + 1) - 2 ** 256
并不会发出溢出。同理 (3 / 2) * 2
也没有产生取整导致精度丢失。
字符串字面量
字符串字面量有以下表示方式:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeStringLiterals {
function num() public pure returns (string memory, string memory, string memory, string memory, string memory, string memory, string memory, string memory, string memory, string memory, string memory, string memory) {
return (
"hi", // 使用双引号
'hello', // 使用单引号
"Hello, " "World!", // 两相紧连的字符串视为一个字符串
'\
add a new line', // 使用 \ 进行断行
'\'', // 转义单引号
"\"", // 转义双引号
'\n', // 转义新行
'\r', // 转义换行
'\t', // 转义制表符
'\x35\x32\x30', // '520':十六进制直接插入字节
'\u4f60\u597d', // '你好':直接插入 unicode 码元,使用 utf-8 编码
unicode"你好 😃" // unicode 字符串,使用 utf-8 编码
);
}
}
字符串字面量长度是确定的,通常会使用 bytesN
变量保存,相比使用 string
类型,gas
消耗更低。
十六进制字面量
Solidity
还有一种跟 string 十六进制字面量,形式如下:
hex"1234"
hex'6789'
hex"1234" hex'6789'
:拼合为hex"12346789"
hex"12_34_67_89"
:每个字节间可以使用下划线分隔
十六进制字面量与字符串字面量很相似(但十六进制字面量不能直接转换为 string 类型),同时又与 0xABCD
的整数字面量很相似。区别见下:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeHexStringLiteral {
function num() public pure returns (bytes8, bytes8, bytes8) {
bytes8 a = 0x0000000000001234;
bytes8 b = hex"1234"; // 十六进制字面量,b = 0x1234000000000000
bytes8 c = "\x12\x34"; // 字符串字面量,等价于 c = hex"1234"
return (a, b, c);
}
}
hex"1234"
相当于字符串字面量 "\x12\x34"
,它们都是左边对齐的。而 0x1234
则是右对齐的。
地址字面量
在上面的 地址类型 一节的代码示例中,我们可以看到转换后的地址 address(uint160(a)) = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc
,后面的字母是大小写混合的。
它与 0x777788889999aaaabbbbccccddddeeeeffffcccc
或 0x777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC
指向的仍是同一个地址。
这种大小写的混合方式,实际上是 EIP-55
提议的一种地址校验编码。如果一个字面量满足该地址校验,那么 Solidity
会将其视为一个地址字面量。
例如:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359
符合
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeAddressLiteral {
function test() public pure {
// 可以,符合 EIP-55 地址校验
address addr = 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359;
// 不可以,不符合 EIP-55 地址校验
address notAddr = 0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359;
}
}
数组字面量
数组字面量是使用 []
括起的一个数组,例如:[-1, 0, 1, 3, f(), 0x5]
。
数组字面量的长度总是可以提前确定的,即[]
内的元素个数。
数字字面量中的第一个元素的类型为该数组字面量的元素类型,因此所有其他的元素都必须能够隐式地转换为该类型:
[1, -1]
是非法的:因为Solidity
认为1
是uint
类型,而-1
并不是合法的uint
值,也不能隐式地转换为int
类型[-1, 1]
是合法的:因为Solidity
认为-1
是int
类型,而1
也为合法的int
类型,最终数组为int
型数组
对于第一种情况,我们可以显式地指定第一个元素的类型,从而间接指定数组字面量的类型:[int(1), -1]
。
以太币单位
字面量数字后加上 wei
gwei
ether
后,可以用于表示以太币数量。
- 最小单位量
wei
相当于数字 1 - 1
gwei
=wei
- 1
ether
=gwei
=wei
1 ether
就是 1 个以太币。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
contract EtherUnit {
function etherUint() public pure returns (uint, uint, uint) {
// 将返回 1, 10^9, 10^18
return (1 wei, 1 gwei, 1 ether);
}
}
时间单位
字面量数字后加上 seconds
minutes
hours
days
weeks
,用于表示时间。
- 1
seconds
相当于数字 1 - 1
minutes
= 60seconds
- 1
hours
= 60minutes
- 1
days
= 24hours
- 1
weeks
= 7days
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
contract TimeUnit {
function timeUnit() public pure returns (uint, uint, uint, uint, uint) {
// 返回 1, 60, 60x60=3600, 60x60x24=86400, 60x60x24x7=604800
return (1 seconds, 1 minutes, 1 hours, 1 days, 1 weeks);
}
}