【区块链安全 | 第十五篇】类型之值类型(二)

news2025/4/2 12:42:27

文章目录

  • 值类型
    • 有理数和整数字面量(Rational and Integer Literals)
    • 字符串字面量和类型(String Literals and Types)
    • Unicode 字面量(Unicode Literals)
    • 十六进制字面量(Hexadecimal Literals)
    • 枚举(Enums)
    • 用户定义值类型

在这里插入图片描述

值类型

有理数和整数字面量(Rational and Integer Literals)

整数字面量由一串数字(范围为 0-9)组成,按十进制解析。例如,69 代表六十九。Solidity 中没有八进制字面量,且前导零是无效的。

十进制小数字面量由一个小数点(.)和至少一个小数点后的数字组成。例如,.11.3 是有效的(但 1. 是无效的)。

科学记数法格式(如 2e10)也被支持,其中尾数(mantissa)可以是小数,但指数必须是整数。字面量 MeE 等价于 M * 10**E。例如,2e10-2e102e-102.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 内计算,可能会发生溢出。

任何可以应用于整数的运算符也可以应用于数字字面量表达式,只要操作数是整数。如果其中任何一个是小数,则不允许使用位运算,并且如果指数是小数,则不允许使用指数运算(因为这可能导致非有理数)。

对于字面量数字作为左操作数(或基数)和整数类型作为右操作数(指数)的移位和指数运算,总是使用 uint256(对于非负字面量)或 int256(对于负字面量)类型,而不管右操作数(指数)的类型。

在 Solidity 0.4.0 版本之前,整数字面量的除法会截断,但现在会转换为有理数,即 5 / 2 不等于 2,而是 2.5

Solidity 为每个有理数提供了一个数字字面量类型。整数字面量和有理数字面量属于数字字面量类型。此外,所有数字字面量表达式(即仅包含数字字面量和运算符的表达式)都属于数字字面量类型。所以表达式 1 + 22 + 1 都属于同一个有理数三的数字字面量类型。

数字字面量表达式一旦与非字面量表达式一起使用,就会被转换为非字面量类型。

在如下代码中,分配给 b 的表达式的值会评估为一个整数。

uint128 a = 1;
uint128 b = 2.5 + a + 0.5;

a 的类型是 uint128,由于 2.5uint128 之间没有共同的类型,Solidity 编译器会拒绝这段代码。

字符串字面量和类型(String Literals and Types)

字符串字面量可以使用双引号或单引号表示(例如 "foo"'bar'),并且它们可以分割成多个连续的部分(例如 "foo" "bar" 等同于 "foobar"),这在处理长字符串时非常有用。与 C 语言不同,字符串字面量并不表示以零结尾;例如 "foo" 只表示三个字节,而不是四个字节。与整数字面量一样,字符串字面量的类型可以变化,但如果符合要求,它们可以隐式转换为 bytes1bytes32 类型,bytesstring 类型。

例如,bytes32 samevar = "stringliteral" 中,字符串字面量会在赋值给 bytes32 类型时以原始字节的形式进行解释。

字符串字面量只能包含可打印的 ASCII 字符,这意味着它们只能包含从 0x200x7E 的字符。

此外,字符串字面量还支持以下转义字符:

  • \<newline>(转义实际的换行符)
  • \\(反斜杠)
  • \'(单引号)
  • \"(双引号)
  • \n(换行符)
  • \r(回车符)
  • \t(制表符)
  • \xNN(十六进制转义,参见下面)
  • \uNNNN(Unicode 转义,参见下面)

xNN 使用十六进制值并插入相应的字节,而 \uNNNN 使用 Unicode 代码点并插入 UTF-8 编码序列。

注意
在版本 0.8.0 之前,还有三个额外的转义序列:\b\f\v。这些转义符在其他编程语言中常见,但在实践中不常用。如果需要使用这些转义符,仍然可以通过十六进制转义插入,即 \x08\x0c\x0b,就像插入其他 ASCII 字符一样。

下面的示例中的字符串长度为十个字节。它以换行符字节开始,接着是双引号、单引号、反斜杠字符,最后是(没有分隔符的)字符序列 abcdef

"\n\"\'\\abc\
def"

任何 Unicode 行终止符(不是换行符的,如 LF、VF、FF、CR、NEL、LS、PS)都被视为终止字符串字面量。只有在换行符前没有 \ 时,换行符才会终止字符串字面量。

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"

十六进制字面量在某些方面类似于字符串字面量,但它们不能隐式转换为字符串类型。

枚举(Enums)

枚举是 Solidity 中创建用户定义类型的一种方式。它们可以显式地转换为任何整数类型,但不允许隐式转换。整数到枚举的显式转换会在运行时检查值是否在枚举的范围内,若不在该范围内则会触发 Panic 错误。枚举至少需要一个成员,且声明时的默认值是第一个成员。枚举不能有超过 256 个成员。

枚举的数据表示与 C 中的枚举相同:选项由从 0 开始的连续无符号整数值表示。

通过使用 type(NameOfEnum).mintype(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;  // 返回最小枚举值
    }
}

枚举也可以在文件级别声明,而不需要在合约或库定义中。

用户定义值类型

用户定义值类型允许对基本值类型进行零成本抽象。这类似于别名,但具有更严格的类型要求。

用户定义值类型使用 type C is V 来定义,其中 C 是新类型的名称,V 必须是内置的值类型(“基础类型”)。C.wrap 函数用于将基础类型转换为自定义类型。类似地,C.unwrap 函数用于将自定义类型转换回基础类型。

C 类型没有任何操作符或附加的成员函数,特别是 == 操作符未定义。显式和隐式的类型转换到其他类型或从其他类型转换是不允许的。

这些类型的值的数据表示继承自基础类型,并且基础类型也用于 ABI。

以下示例演示了一个自定义类型 UFixed256x18,表示一个带有 18 位小数的固定点类型,以及一个用于对该类型执行算术运算的最小库。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;

// 使用用户定义的值类型表示一个 18 小数点、256 位宽的固定点类型。
type UFixed256x18 is uint256;

/// 一个用于对 UFixed256x18 执行固定点运算的最小库。
library FixedMath {
    uint constant multiplier = 10**18;

    /// 添加两个 UFixed256x18 数字。如果溢出,则回退,依赖于 uint256 的检查算术。
    function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
    }

    /// 将 UFixed256x18 与 uint256 相乘。如果溢出,则回退,依赖于 uint256 的检查算术。
    function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
    }

    /// 取 UFixed256x18 数字的下限(即不超过 `a` 的最大整数)。
    function floor(UFixed256x18 a) internal pure returns (uint256) {
        return UFixed256x18.unwrap(a) / multiplier;
    }

    /// 将 uint256 转换为一个值相同的 UFixed256x18。
    /// 如果整数太大,则回退。
    function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(a * multiplier);
    }
}

注意: UFixed256x18.wrapFixedMath.toUFixed256x18 有相同的函数签名,但执行了两种非常不同的操作:UFixed256x18.wrap 函数返回一个具有相同数据表示的 UFixed256x18,而 toUFixed256x18 返回一个数值相同的 UFixed256x18

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325559.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu修改用户名

修改用户名&#xff1a; 1.CTRL ALT T 快捷键打开终端&#xff0c;输入‘sudo su’ 转为root用户。 2.输入‘ gredit /etc/passwd ’&#xff0c;修改用户名&#xff0c;只修改用户名&#xff0c;后面的全名、目录等不修改。 3.输入 ‘ gedit /etc/shadow ’ 和 ‘ gedit /etc/…

Windows 系统下多功能免费 PDF 编辑工具详解

IceCream PDF Editor是一款极为实用且操作简便的PDF文件编辑工具&#xff0c;它完美适配Windows操作系统。其用户界面设计得十分直观&#xff0c;哪怕是初次接触的用户也能快速上手。更为重要的是&#xff0c;该软件具备丰富多样的强大功能&#xff0c;能全方位满足各类PDF编辑…

UE学习记录part11

第14节 breakable actors 147 destructible meshes a geometry collection is basically a set of static meshes that we get after we fracture a mesh. 几何体集合基本上是我们在断开网格后获得的一组静态网格。 选中要破碎的网格物品&#xff0c;创建集合 可以选择不同的…

Redis-07.Redis常用命令-集合操作命令

一.集合操作命令 SADD key member1 [member2]&#xff1a; sadd set1 a b c d sadd set1 a 0表示没有添加成功&#xff0c;因为集合中已经有了这个元素了&#xff0c;因此无法重复添加。 SMEMBERS key: smembers set1 SCARD key&#xff1a; scard set1 SADD key member1 …

vscode 源代码管理

https://code.visualstudio.com/updates/v1_92#_source-control 您可以通过切换 scm.showHistoryGraph 设置来禁用传入/传出更改的图形可视化。

iOS审核被拒:Missing privacy manifest 第三方库添加隐私声明文件

问题&#xff1a; iOS提交APP审核被拒&#xff0c;苹果开发者网页显示二进制错误&#xff0c;收到的邮件显示的详细信息如下图: 分析&#xff1a; 从上面信息能看出第三方SDK库必须要包含一个隐私文件&#xff0c;去第三方库更新版本。 几经查询资料得知&#xff0c;苹果在…

【LeetCode Solutions】LeetCode 101 ~ 105 题解

CONTENTS LeetCode 101. 对称二叉树&#xff08;简单&#xff09;LeetCode 102. 二叉树的层序遍历&#xff08;中等&#xff09;LeetCode 103. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09;LeetCode 104. 二叉树的最大深度&#xff08;简单&#xff09;LeetCode 105. 从…

Orpheus-TTS 介绍,新一代开源文本转语音

Orpheus-TTS 是由 Canopy Labs 团队于2025年3月19日发布的开源文本转语音&#xff08;TTS&#xff09;模型&#xff0c;其技术突破集中在超低延迟、拟人化情感表达与实时流式生成三大领域。以下从技术架构、核心优势、应用场景、对比分析、开发背景及最新进展等多维度展开深入解…

Java数据结构-栈和队列

目录 1. 栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环 3. 括号匹配 4. 逆波兰表达式求值 5. 出栈入栈次序匹配 6. 最小栈 1.5 概念区分 2. 队列(Queue) 2.1 概念 2.2 队列的使用 2.3 队列模拟实…

权重衰减-笔记

《动手学深度学习》-4.5-笔记 权重衰减就像给模型“勒紧裤腰带”&#xff0c;不让它太贪心、不让它学太多。 你在学英语单词&#xff0c;别背太多冷门单词&#xff0c;只背常见的就行&#xff0c;这样考试时更容易拿分。” —— 这其实就是在“限制你学的内容复杂度”。 在…

Hyperliquid 遇袭「拔网线」、Polymarket 遭治理攻击「不作为」,从双平台危机看去中心化治理的进化阵痛

作者&#xff1a;Techub 热点速递 撰文&#xff1a;Glendon&#xff0c;Techub News 继 3 月 12 日「Hyperliquid 50 倍杠杆巨鲸」引发的 Hyperliquid 清算事件之后&#xff0c;3 月 26 日 晚间&#xff0c;Hyperliquid 再次遭遇了一场针对其流动性和治理模式的「闪电狙击」。…

软考笔记6——结构化开发方法

第六章节——结构化开发方法 结构化开发方法 第六章节——结构化开发方法一、系统分析与设计概述1. 系统分析概述2. 系统设计的基本原理3. 系统总体结构设计 二、结构化分析方法1. 结构化分析方法概述2. 数据流图(DFD)3. 数据字典 三、结构化设计方法&#xff08;了解&#xff…

一种C# Winform的UI处理

效果 圆角 阴影 突出按钮 说明 这是一种另类的处理&#xff0c;不是多层窗口 也不是WPF 。这种方式的特点是比较简单&#xff0c;例如圆角、阴影、按钮等特别容易修改过。其实就是html css DirectXForm。 在VS中如下 圆角和阴影 然后编辑这个窗体的Html模板&#xff0c…

为什么视频文件需要压缩?怎样压缩视频体积即小又清晰?

在日常生活中&#xff0c;无论是为了节省存储空间、便于分享还是提升上传速度&#xff0c;我们常常会遇到需要压缩视频的情况。本文将介绍为什么视频需要压缩&#xff0c;压缩视频的好处与坏处&#xff0c;并教你如何使用简鹿视频格式转换器轻松完成MP4视频文件的压缩。 为什么…

Nginx — Nginx处理Web请求机制解析

一、Nginx请求默认页面资源 1、配置文件详解 修改端口号为8080并重启服务&#xff1a; 二、Nginx进程模型 1、nginx常用命令解析 master进程&#xff1a;主进程&#xff08;只有一个&#xff09; worker进程&#xff1a;工作进程&#xff08;可以有多个&#xff0c;默认只有一…

5.0 WPF的基础介绍1-Grid,Stack,button

WPF: Window Presentation Foundation. WPF与WinForms的对比如下&#xff1a; 特性WinFormsWPF技术基础基于传统的GDI&#xff08;图形设备接口&#xff09;基于DirectX&#xff0c;支持硬件加速的矢量渲染UI设计方式拖拽控件事件驱动代码&#xff08;简单但局限&#xff09;…

Docker 端口映射原理

在 Docker 中&#xff0c;默认情况下容器无法直接与外部网络通信。 为了使外部网络能够访问容器内的服务&#xff0c;Docker 提供了端口映射功能&#xff0c;通过将宿主机的端口映射到容器内的端口&#xff0c;外部可以通过宿主机的IP和端口访问容器内的服务 以下通过动手演示…

SDL —— 将sdl渲染画面嵌入Qt窗口显示(附:源码)

🔔 SDL/SDL2 相关技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 效果 使用QWidget加载了SDL的窗口,渲染器使用硬件加速跑GPU的。支持Qt窗口缩放或显示隐藏均不影响SDL的图像刷新。   操作步骤 1、在创建C++空工程时加入SDL,引入头文件时需…

算法每日一练 (23)

&#x1f4a2;欢迎来到张翊尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 算法每日一练 (23)最大正方形题目描述解题思路解题代码…

UE5学习笔记 FPS游戏制作28 显式玩家子弹数

文章目录 添加变量修改ShootOnce方法&#xff0c;设计时减少子弹&#xff0c;没有子弹不能开枪在UI上显示 添加变量 在Gun类中添加BulletNum和ClipSize两个参数 BulletNum是当前还有多少子弹&#xff0c;ClipSize是一个弹匣多少子弹 Rifle的ClipSzie设置为30&#xff0c;Laun…