字面量 值类型

字面量即在代码中直接给出的值,例如代码中的 0x01, "Hello, World!" 等都属于字面量,甚至可以包含运算:1 + 2

代码中给出的字面量经过运算、处理后保存在字面量类型中。

数字字面量

数字字面量可以由以下方式给出:

ValueTypeNumberLiterals.sol
运行
复制
// 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 忽略。

只包含数字字面量和运算符的表达式,也是一个字面量:

ValueTypeNumberLiteralsCompute.sol
运行
复制
// 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 也没有产生取整导致精度丢失。

字符串字面量

字符串字面量有以下表示方式:

ValueTypeStringLiterals.sol
运行
复制
// 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 的整数字面量很相似。区别见下:

ValueTypeHexStringLiteral.sol
运行
复制
// 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,后面的字母是大小写混合的。

它与 0x777788889999aaaabbbbccccddddeeeeffffcccc0x777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC 指向的仍是同一个地址。

这种大小写的混合方式,实际上是 EIP-55 提议的一种地址校验编码。如果一个字面量满足该地址校验,那么 Solidity 会将其视为一个地址字面量。

例如:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 符合

ValueTypeAddressLiteral.sol
运行
复制
// 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 认为 1uint 类型,而 -1 并不是合法的 uint 值,也不能隐式地转换为 int 类型
  • [-1, 1] 是合法的:因为 Solidity 认为 -1int 类型,而 1 也为合法的 int 类型,最终数组为 int 型数组

对于第一种情况,我们可以显式地指定第一个元素的类型,从而间接指定数组字面量的类型:[int(1), -1]

以太币单位

字面量数字后加上 wei gwei ether 后,可以用于表示以太币数量。

  • 最小单位量 wei 相当于数字 1
  • 1 gwei = 10910^9 wei
  • 1 ether = 10910^9 gwei = 101810^{18} wei

1 ether 就是 1 个以太币。

EtherUnit.sol
运行
复制
// 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 = 60 seconds
  • 1 hours = 60 minutes
  • 1 days = 24 hours
  • 1 weeks = 7 days
TimeUnit.sol
运行
复制
// 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);
    }
}