文章目录

值类型
有理数和整数字面量(Rational and Integer Literals)
整数字面量由一串数字(范围为 0-9)组成,并按十进制解析。例如,69 表示六十九。Solidity 中没有八进制字面量,且前导零无效。
十进制小数字面量由一个小数点(.)和至少一个小数点后的数字组成。例如,.1 和 1.3 是有效的(但 1. 是无效的)。
Solidity 也支持科学记数法格式(如 2e10),其中尾数(mantissa)可以是小数,但指数必须是整数。字面量 MeE 等价于 M * 10**E。例如,2e10、-2e10、2e-10 和 2.5e1 都是有效的。
我们可以使用下划线分隔数字字面量中的数字,以提高可读性。例如,十进制的 123_000、十六进制的 0x2eff_abde、科学记数法表示的 1_2e345_678 都是有效的。下划线只能放在两个数字之间,且只能使用一个连续的下划线。数字字面量中的下划线不会改变字面量的语义,它们仅用于增强可读性。
数字字面量表达式具有任意精度,直到它们被转换为非字面量类型(即与其他非字面量表达式一起使用,或通过显式转换)。这意味着数字字面量表达式中的计算不会溢出,除法操作也不会截断。
例如,(2^800 + 1) - 2^800 的结果是常量 1(类型为 uint8),尽管中间结果无法完全适应机器字长。此外,.5 * 8 结果是整数 4(尽管其中使用了非整数)。
注意:
虽然大多数运算符在应用于字面量时会产生字面量表达式,但有些运算符不遵循这一模式:
-
三元运算符(… ? … : …)
-
数组下标(
<array>[<index>])
例如,你可能期待像 255 + (true ? 1 : 0) 或 255 + [1, 2, 3][0] 这样的表达式等价于直接使用字面量 256,但实际上它们会在类型 uint8 范围内计算,可能会发生溢出。
如图:

任何可以应用于整数的运算符也可以应用于数字字面量表达式,只要操作数是整数。如果其中任何一个是小数,则不允许使用位运算,并且如果指数是小数,则不能使用指数运算(因为这可能导致非有理数结果)。
对于字面量数字作为左操作数(或基数)与整数类型作为右操作数(指数)的移位和指数运算,Solidity 总是使用 uint256(对于非负字面量)或 int256(对于负字面量)类型,无论右操作数(指数)的类型是什么。
在 Solidity 0.4.0 版本之前,整数字面量的除法会进行截断,但现在会转换为有理数类型,即 5 / 2 不等于 2,而是 2.5。
Solidity 为每个有理数提供了一个数字字面量类型。整数字面量和有理数字面量属于数字字面量类型。此外,所有数字字面量表达式(即仅包含数字字面量和运算符的表达式)都属于数字字面量类型。所以表达式 1 + 2 和 2 + 1 都属于同一个有理数类型。
数字字面量表达式一旦与非字面量表达式一起使用,就会被转换为非字面量类型。
在以下代码中,分配给 b 的表达式会评估为一个整数:
uint128 a = 1;
uint128 b = 2.5 + a + 0.5;
a 的类型是 uint128,由于 2.5 和 uint128 之间没有共同的类型,Solidity 编译器会拒绝这段代码。
如图所示:

字符串字面量和类型(String Literals and Types)
字符串字面量可以使用双引号或单引号表示(例如 “foo” 或 ‘bar’),并且它们可以分割成多个连续的部分(例如 “foo” “bar” 等同于 “foobar”),这在处理长字符串时非常有用。与 C 语言不同,字符串字面量不表示以零结尾;例如,“foo” 只表示三个字节,而不是四个字节。与整数字面量类似,字符串字面量的类型也可以变化,但它们可以隐式转换为 bytes1 到 bytes32 类型,bytes 和 string 类型。
例如,bytes32 samevar = “stringliteral” 中,字符串字面量会在赋值给 bytes32 类型时以原始字节的形式进行解释。
字符串字面量只能包含可打印的 ASCII 字符,这意味着它们只能包含从 0x20 到 0x7E 范围的字符。
此外,字符串字面量还支持以下转义字符:
-
\<newline>(转义实际的换行符) -
\\(反斜杠) -
\'(单引号) -
\"(双引号) -
\n(换行符) -
\r(回车符) -
\t(制表符) -
\xNN(十六进制转义,参见下面) -
\uNNNN(Unicode 转义,参见下面)
xNN 使用十六进制值并插入相应的字节,而 \uNNNN 使用 Unicode 代码点并插入 UTF-8 编码序列。
注意:
在 Solidity 0.8.0 之前,还有三个额外的转义序列:\b、\f 和 \v。这些转义符在其他编程语言中常见,但在实际应用中不常用。如果需要使用这些转义符,可以通过十六进制转义插入,即 \x08、\x0c 和 \x0b,就像插入其他 ASCII 字符一样。
Unicode 字面量(Unicode Literals)
普通的字符串字面量只能包含 ASCII 字符,而 Unicode 字面量(以 unicode 关键字为前缀)可以包含任何有效的 UTF-8 序列。它们同样支持与普通字符串字面量相同的转义序列。
例如:
string memory a = unicode"Hello 😃";
十六进制字面量(Hexadecimal Literals)
十六进制字面量以 hex 关键字为前缀,并被双引号或单引号包围(例如 hex"001122FF" 或 hex’0011_22_FF’)。它们的内容必须是十六进制数字,并且可以选择性地使用单个下划线作为字节边界的分隔符。字面量的值将是十六进制序列的二进制表示。
多个由空格分隔的十六进制字面量会被连接成一个字面量。例如,hex"00112233" hex"44556677" 等同于 hex"0011223344556677"。
十六进制字面量与字符串字面量类似,但它们不能隐式转换为字符串类型。
示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HexLiteralExample {
// 使用十六进制字面量定义字节数据
bytes constant hexData1 = hex"001122FF";
bytes constant hexData2 = hex"4455_66_77"; // 可使用下划线分隔字节
// 拼接两个十六进制字面量(会在编译时合并)
bytes constant concatenated = hex"00112233" hex"44556677";
function getHexData1() external pure returns (bytes memory) {
return hexData1;
}
function getHexData2() external pure returns (bytes memory) {
return hexData2;
}
function getConcatenated() external pure returns (bytes memory) {
return concatenated;
}
}


枚举(Enums)
枚举是 Solidity 中创建用户定义类型的一种方式。它们可以显式地转换为任何整数类型,但不允许隐式转换。整数到枚举的显式转换会在运行时检查值是否在枚举的范围内,如果不在该范围内则会触发 Panic 错误。枚举至少需要一个成员,且声明时的默认值是第一个成员。枚举的成员数量不能超过 256 个。
枚举的数据表示与 C 语言中的枚举类似:选项由从 0 开始的连续无符号整数值表示。
通过使用 type(NameOfEnum).min 和 type(NameOfEnum).max,可以获得给定枚举的最小值和最大值。
例如:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;
contract test {
// 定义枚举类型 ActionChoices,表示四个动作选项
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
// 创建一个 ActionChoices 类型的变量 choice 来存储当前选择
ActionChoices choice;
// 声明一个常量 defaultChoice,默认为 GoStraight
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
// public 函数 setGoStraight 用于将 choice 设置为 GoStraight
function setGoStraight() public {
choice = ActionChoices.GoStraight;
}
// public 函数 getChoice 返回当前的 choice,注意:枚举类型在外部函数中会转换为 uint8 类型
// 由于枚举类型不是 ABI 的一部分,所以 "getChoice" 的签名会变成 "getChoice() returns (uint8)"
function getChoice() public view returns (ActionChoices) {
return choice;
}
// public 函数 getDefaultChoice 返回默认值 GoStraight 对应的整数值
// 返回的是 uint 类型的数字,表示 GoStraight 在枚举中的整数值
function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}
// public 函数 getLargestValue 返回枚举类型中最大的值
function getLargestValue() public pure returns (ActionChoices) {
return type(ActionChoices).max; // 返回最大枚举值
}
// public 函数 getSmallestValue 返回枚举类型中最小的值
function getSmallestValue() public pure returns (ActionChoices) {
return type(ActionChoices).min; // 返回最小枚举值
}
}
枚举也可以在文件级别声明,而不需要在合约或库定义中。
用户定义值类型
用户定义值类型(User-Defined Value Types)允许开发者在不引入额外运行时开销的前提下,对基础类型(如 uint256、address 等)进行类型封装,实现更强的类型安全。这种机制提供了类似类型别名的功能,但在类型系统中更加严格。
用户定义值类型的语法为:
type C is V;
其中 C 是新定义的类型名,V 是必须来自内建的值类型(即基础类型)。
可以使用以下函数进行类型转换:
-
C.wrap(value):将基础类型值转换为用户定义类型。
-
C.unwrap(value):将用户定义类型值转换为基础类型。
需要注意的是:
-
用户定义的值类型不继承基础类型的任何操作符或成员函数。
-
运算符(例如 ==)是未定义的,必须显式调用 unwrap 后再进行比较或其他操作。
-
不允许任何隐式或显式的类型转换,除非通过 wrap / unwrap。
这些类型的内部表示与其基础类型一致,并且在 ABI 编码/解码时使用基础类型的方式。
示例合约代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// 定义一个用户自定义类型 U256,基于 uint256
type U256 is uint256;
contract UDVTExample {
// 定义一个变量,类型为 U256
U256 public myValue;
// 设置值时需使用 wrap
function setValue(uint256 _value) public {
myValue = U256.wrap(_value);
}
// 获取值时需使用 unwrap
function getValue() public view returns (uint256) {
return U256.unwrap(myValue);
}
// 示例函数,使用 unwrap 进行比较
function isEqual(uint256 _value) public view returns (bool) {
return U256.unwrap(myValue) == _value;
}
}





被折叠的 条评论
为什么被折叠?



